Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
50824a7245 | ||
![]() |
6f2953f3a7 | ||
![]() |
dd3f007582 | ||
![]() |
a4b2b093fc | ||
![]() |
0fbf56219f |
@@ -379,6 +379,17 @@ common/views/components/poll-editor.vue:
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "カスタム絵文字"
|
||||
people: "人"
|
||||
animals-and-nature: "動物&自然"
|
||||
food-and-drink: "食べ物&飲み物"
|
||||
activity: "アクティビティ"
|
||||
travel-and-places: "場所"
|
||||
objects: "物"
|
||||
symbols: "記号"
|
||||
flags: "旗"
|
||||
|
||||
common/views/components/signin.vue:
|
||||
username: "ユーザー名"
|
||||
password: "パスワード"
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.48.1",
|
||||
"clientVersion": "2.0.11733",
|
||||
"version": "10.49.0",
|
||||
"clientVersion": "2.0.11740",
|
||||
"codename": "nighthike",
|
||||
"main": "./built/index.js",
|
||||
"private": true,
|
||||
|
@@ -146,6 +146,8 @@
|
||||
function refresh() {
|
||||
localStorage.setItem('shouldFlush', 'false');
|
||||
|
||||
localStorage.removeItem('locale');
|
||||
|
||||
// Random
|
||||
localStorage.setItem('salt', Math.random().toString().substr(2, 8));
|
||||
|
||||
|
200
src/client/app/common/views/components/emoji-picker.vue
Normal file
200
src/client/app/common/views/components/emoji-picker.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<div class="prlncendiewqqkrevzeruhndoakghvtx">
|
||||
<header>
|
||||
<button v-for="category in categories"
|
||||
:title="category.text"
|
||||
@click="go(category.ref)"
|
||||
:class="{ active: category.isActive }"
|
||||
>
|
||||
<fa :icon="category.icon" fixed-width/>
|
||||
</button>
|
||||
</header>
|
||||
<div class="emojis" ref="emojis" @scroll.passive="onScroll">
|
||||
<section v-for="category in categories" :ref="category.ref">
|
||||
<header><fa :icon="category.icon" fixed-width/> {{ category.text }}</header>
|
||||
<div v-if="category.name">
|
||||
<button v-for="emoji in Object.entries(lib).filter(([k, v]) => v.category === category.name)"
|
||||
:title="emoji[0]"
|
||||
@click="chosen(emoji[1].char)"
|
||||
>
|
||||
<mk-emoji :emoji="emoji[1].char"/>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button v-for="emoji in customEmojis"
|
||||
:title="emoji.name"
|
||||
@click="chosen(`:${emoji.name}:`)"
|
||||
>
|
||||
<img :src="emoji.url" :alt="emoji.name"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import { lib } from 'emojilib';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/emoji-picker.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
lib,
|
||||
customEmojis: [],
|
||||
categories: [{
|
||||
ref: 'customEmojiSection',
|
||||
text: this.$t('custom-emoji'),
|
||||
icon: ['fas', 'asterisk'],
|
||||
isActive: true
|
||||
}, {
|
||||
name: 'people',
|
||||
ref: 'peopleSection',
|
||||
text: this.$t('people'),
|
||||
icon: ['far', 'laugh'],
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'animals_and_nature',
|
||||
ref: 'animalsAndNatureSection',
|
||||
text: this.$t('animals-and-nature'),
|
||||
icon: ['fas', 'leaf'],
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'food_and_drink',
|
||||
ref: 'foodAndDrinkSection',
|
||||
text: this.$t('food-and-drink'),
|
||||
icon: ['fas', 'utensils'],
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'activity',
|
||||
ref: 'activitySection',
|
||||
text: this.$t('activity'),
|
||||
icon: ['fas', 'futbol'],
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'travel_and_places',
|
||||
ref: 'travelAndPlacesSection',
|
||||
text: this.$t('travel-and-places'),
|
||||
icon: ['fas', 'city'],
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'objects',
|
||||
ref: 'objectsSection',
|
||||
text: this.$t('objects'),
|
||||
icon: ['fas', 'poo-storm'],
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'symbols',
|
||||
ref: 'symbolsSection',
|
||||
text: this.$t('symbols'),
|
||||
icon: ['far', 'heart'],
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'flags',
|
||||
ref: 'flagsSection',
|
||||
text: this.$t('flags'),
|
||||
icon: ['far', 'flag'],
|
||||
isActive: false
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
|
||||
},
|
||||
|
||||
methods: {
|
||||
go(ref) {
|
||||
this.$refs.emojis.scrollTop = this.$refs[ref][0].offsetTop;
|
||||
},
|
||||
|
||||
onScroll(e) {
|
||||
const section = this.categories.forEach(x => {
|
||||
const top = e.target.scrollTop;
|
||||
const el = this.$refs[x.ref][0];
|
||||
x.isActive = el.offsetTop <= top && el.offsetTop + el.offsetHeight > top;
|
||||
});
|
||||
},
|
||||
|
||||
chosen(emoji) {
|
||||
this.$emit('chosen', emoji);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.prlncendiewqqkrevzeruhndoakghvtx
|
||||
width 350px
|
||||
background var(--face)
|
||||
|
||||
> header
|
||||
display flex
|
||||
|
||||
> button
|
||||
flex 1
|
||||
padding 10px 0
|
||||
font-size 16px
|
||||
color var(--text)
|
||||
transition color 0.2s ease
|
||||
|
||||
&:hover
|
||||
color var(--textHighlighted)
|
||||
transition color 0s
|
||||
|
||||
&.active
|
||||
color var(--primary)
|
||||
transition color 0s
|
||||
|
||||
> .emojis
|
||||
height 300px
|
||||
overflow-y auto
|
||||
overflow-x hidden
|
||||
|
||||
> section
|
||||
> header
|
||||
position sticky
|
||||
top 0
|
||||
left 0
|
||||
z-index 1
|
||||
padding 8px
|
||||
background var(--faceHeader)
|
||||
color var(--text)
|
||||
font-size 12px
|
||||
|
||||
> div
|
||||
display grid
|
||||
grid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
|
||||
gap 4px
|
||||
padding 8px
|
||||
|
||||
> button
|
||||
padding 0
|
||||
width 100%
|
||||
|
||||
&:before
|
||||
content ''
|
||||
display block
|
||||
width 1px
|
||||
height 0
|
||||
padding-bottom 100%
|
||||
|
||||
&:hover
|
||||
> *
|
||||
transform scale(1.2)
|
||||
transition transform 0s
|
||||
|
||||
> *
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
width 100%
|
||||
height 100%
|
||||
font-size 28px
|
||||
transition transform 0.2s ease
|
||||
pointer-events none
|
||||
|
||||
</style>
|
@@ -22,7 +22,7 @@ export default Vue.extend({
|
||||
},
|
||||
customEmojis: {
|
||||
required: false,
|
||||
default: []
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="gcafiosrssbtbnbzqupfmglvzgiaipyv">
|
||||
<x-picker @chosen="chosen"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import contains from '../../../common/scripts/contains';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XPicker: () => import('../../../common/views/components/emoji-picker.vue').then(m => m.default)
|
||||
},
|
||||
|
||||
props: {
|
||||
x: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
y: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const width = this.$el.offsetWidth;
|
||||
const height = this.$el.offsetHeight;
|
||||
|
||||
let x = this.x;
|
||||
let y = this.y;
|
||||
|
||||
if (x + width - window.pageXOffset > window.innerWidth) {
|
||||
x = window.innerWidth - width + window.pageXOffset;
|
||||
}
|
||||
|
||||
if (y + height - window.pageYOffset > window.innerHeight) {
|
||||
y = window.innerHeight - height + window.pageYOffset;
|
||||
}
|
||||
|
||||
this.$el.style.left = x + 'px';
|
||||
this.$el.style.top = y + 'px';
|
||||
|
||||
Array.from(document.querySelectorAll('body *')).forEach(el => {
|
||||
el.addEventListener('mousedown', this.onMousedown);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
onMousedown(e) {
|
||||
e.preventDefault();
|
||||
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
|
||||
return false;
|
||||
},
|
||||
|
||||
chosen(emoji) {
|
||||
this.$emit('chosen', emoji);
|
||||
this.close();
|
||||
},
|
||||
|
||||
close() {
|
||||
Array.from(document.querySelectorAll('body *')).forEach(el => {
|
||||
el.removeEventListener('mousedown', this.onMousedown);
|
||||
});
|
||||
|
||||
this.$emit('closed');
|
||||
this.destroyDom();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.gcafiosrssbtbnbzqupfmglvzgiaipyv
|
||||
position fixed
|
||||
top 0
|
||||
left 0
|
||||
z-index 3000
|
||||
box-shadow 0 2px 12px 0 rgba(0, 0, 0, 0.3)
|
||||
|
||||
</style>
|
@@ -15,21 +15,26 @@
|
||||
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
||||
</div>
|
||||
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
||||
<textarea :class="{ with: (files.length != 0 || poll) }"
|
||||
ref="text" v-model="text" :disabled="posting"
|
||||
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
|
||||
v-autocomplete="'text'"
|
||||
></textarea>
|
||||
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
|
||||
<x-draggable :list="files" :options="{ animation: 150 }">
|
||||
<div v-for="file in files" :key="file.id">
|
||||
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
|
||||
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
|
||||
</div>
|
||||
</x-draggable>
|
||||
<p class="remain">{{ 4 - files.length }}/4</p>
|
||||
<div class="textarea">
|
||||
<textarea :class="{ with: (files.length != 0 || poll) }"
|
||||
ref="text" v-model="text" :disabled="posting"
|
||||
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
|
||||
v-autocomplete="'text'"
|
||||
></textarea>
|
||||
<button class="emoji" @click="emoji" ref="emoji">
|
||||
<fa :icon="['far', 'laugh']"/>
|
||||
</button>
|
||||
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
|
||||
<x-draggable :list="files" :options="{ animation: 150 }">
|
||||
<div v-for="file in files" :key="file.id">
|
||||
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
|
||||
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
|
||||
</div>
|
||||
</x-draggable>
|
||||
<p class="remain">{{ 4 - files.length }}/4</p>
|
||||
</div>
|
||||
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
|
||||
</div>
|
||||
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
|
||||
</div>
|
||||
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
|
||||
<button class="upload" :title="$t('attach-media-from-local')" @click="chooseFile"><fa icon="upload"/></button>
|
||||
@@ -377,6 +382,19 @@ export default Vue.extend({
|
||||
this.visibleUsers = erase(user, this.visibleUsers);
|
||||
},
|
||||
|
||||
async emoji() {
|
||||
const Picker = await import('./emoji-picker-dialog.vue').then(m => m.default);
|
||||
const button = this.$refs.emoji;
|
||||
const rect = button.getBoundingClientRect();
|
||||
const vm = this.$root.new(Picker, {
|
||||
x: button.offsetWidth + rect.left + window.pageXOffset,
|
||||
y: rect.top + window.pageYOffset
|
||||
});
|
||||
vm.$once('chosen', emoji => {
|
||||
insertTextAtCursor(this.$refs.text, emoji);
|
||||
});
|
||||
},
|
||||
|
||||
post() {
|
||||
this.posting = true;
|
||||
|
||||
@@ -469,7 +487,7 @@ export default Vue.extend({
|
||||
|
||||
> .content
|
||||
> input
|
||||
> textarea
|
||||
> .textarea > textarea
|
||||
display block
|
||||
width 100%
|
||||
padding 12px
|
||||
@@ -498,27 +516,108 @@ export default Vue.extend({
|
||||
> input
|
||||
margin-bottom 8px
|
||||
|
||||
> textarea
|
||||
margin 0
|
||||
max-width 100%
|
||||
min-width 100%
|
||||
min-height 84px
|
||||
> .textarea
|
||||
> .emoji
|
||||
position absolute
|
||||
top 0
|
||||
right 0
|
||||
padding 10px
|
||||
font-size 18px
|
||||
color var(--text)
|
||||
opacity 0.5
|
||||
|
||||
&:hover
|
||||
& + *
|
||||
& + * + *
|
||||
border-color var(--primaryAlpha02)
|
||||
transition border-color .1s ease
|
||||
&:hover
|
||||
color var(--textHighlighted)
|
||||
opacity 1
|
||||
|
||||
&:focus
|
||||
& + *
|
||||
& + * + *
|
||||
border-color var(--primaryAlpha05)
|
||||
transition border-color 0s ease
|
||||
&:active
|
||||
color var(--primary)
|
||||
opacity 1
|
||||
|
||||
&.with
|
||||
border-bottom solid 1px var(--primaryAlpha01) !important
|
||||
border-radius 4px 4px 0 0
|
||||
> textarea
|
||||
margin 0
|
||||
max-width 100%
|
||||
min-width 100%
|
||||
min-height 84px
|
||||
|
||||
&:hover
|
||||
& + * + *
|
||||
& + * + * + *
|
||||
border-color var(--primaryAlpha02)
|
||||
transition border-color .1s ease
|
||||
|
||||
&:focus
|
||||
& + * + *
|
||||
& + * + * + *
|
||||
border-color var(--primaryAlpha05)
|
||||
transition border-color 0s ease
|
||||
|
||||
& + .emoji
|
||||
opacity 0.7
|
||||
|
||||
&.with
|
||||
border-bottom solid 1px var(--primaryAlpha01) !important
|
||||
border-radius 4px 4px 0 0
|
||||
|
||||
> .files
|
||||
margin 0
|
||||
padding 0
|
||||
background var(--desktopPostFormTextareaBg)
|
||||
border solid 1px var(--primaryAlpha01)
|
||||
border-top none
|
||||
border-radius 0 0 4px 4px
|
||||
transition border-color .3s ease
|
||||
|
||||
&.with
|
||||
border-bottom solid 1px var(--primaryAlpha01) !important
|
||||
border-radius 0
|
||||
|
||||
> .remain
|
||||
display block
|
||||
position absolute
|
||||
top 8px
|
||||
right 8px
|
||||
margin 0
|
||||
padding 0
|
||||
color var(--primaryAlpha04)
|
||||
|
||||
> div
|
||||
padding 4px
|
||||
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
clear both
|
||||
|
||||
> div
|
||||
float left
|
||||
border solid 4px transparent
|
||||
cursor move
|
||||
|
||||
&:hover > .remove
|
||||
display block
|
||||
|
||||
> .img
|
||||
width 64px
|
||||
height 64px
|
||||
background-size cover
|
||||
background-position center center
|
||||
|
||||
> .remove
|
||||
display none
|
||||
position absolute
|
||||
top -6px
|
||||
right -6px
|
||||
width 16px
|
||||
height 16px
|
||||
cursor pointer
|
||||
|
||||
> .mk-poll-editor
|
||||
background var(--desktopPostFormTextareaBg)
|
||||
border solid 1px var(--primaryAlpha01)
|
||||
border-top none
|
||||
border-radius 0 0 4px 4px
|
||||
transition border-color .3s ease
|
||||
|
||||
> .visibleUsers
|
||||
margin-bottom 8px
|
||||
@@ -541,66 +640,6 @@ export default Vue.extend({
|
||||
margin-right 8px
|
||||
white-space nowrap
|
||||
|
||||
> .files
|
||||
margin 0
|
||||
padding 0
|
||||
background var(--desktopPostFormTextareaBg)
|
||||
border solid 1px var(--primaryAlpha01)
|
||||
border-top none
|
||||
border-radius 0 0 4px 4px
|
||||
transition border-color .3s ease
|
||||
|
||||
&.with
|
||||
border-bottom solid 1px var(--primaryAlpha01) !important
|
||||
border-radius 0
|
||||
|
||||
> .remain
|
||||
display block
|
||||
position absolute
|
||||
top 8px
|
||||
right 8px
|
||||
margin 0
|
||||
padding 0
|
||||
color var(--primaryAlpha04)
|
||||
|
||||
> div
|
||||
padding 4px
|
||||
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
clear both
|
||||
|
||||
> div
|
||||
float left
|
||||
border solid 4px transparent
|
||||
cursor move
|
||||
|
||||
&:hover > .remove
|
||||
display block
|
||||
|
||||
> .img
|
||||
width 64px
|
||||
height 64px
|
||||
background-size cover
|
||||
background-position center center
|
||||
|
||||
> .remove
|
||||
display none
|
||||
position absolute
|
||||
top -6px
|
||||
right -6px
|
||||
width 16px
|
||||
height 16px
|
||||
cursor pointer
|
||||
|
||||
> .mk-poll-editor
|
||||
background var(--desktopPostFormTextareaBg)
|
||||
border solid 1px var(--primaryAlpha01)
|
||||
border-top none
|
||||
border-radius 0 0 4px 4px
|
||||
transition border-color .3s ease
|
||||
|
||||
> .mk-uploader
|
||||
margin 8px 0 0 0
|
||||
padding 8px
|
||||
|
@@ -1,16 +1,48 @@
|
||||
<template>
|
||||
<div class="mkw-post-form">
|
||||
<template v-if="props.design == 0">
|
||||
<p class="title"><fa icon="pencil-alt"/>{{ $t('title') }}</p>
|
||||
</template>
|
||||
<textarea :disabled="posting" v-model="text" @keydown="onKeydown" :placeholder="placeholder"></textarea>
|
||||
<button @click="post" :disabled="posting">{{ $t('note') }}</button>
|
||||
<div>
|
||||
<mk-widget-container :show-header="props.design == 0">
|
||||
<template slot="header"><fa icon="pencil-alt"/>{{ $t('title') }}</template>
|
||||
|
||||
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body">
|
||||
<div class="textarea">
|
||||
<textarea
|
||||
:disabled="posting"
|
||||
v-model="text"
|
||||
@keydown="onKeydown"
|
||||
@paste="onPaste"
|
||||
:placeholder="placeholder"
|
||||
ref="text"
|
||||
v-autocomplete="'text'"
|
||||
></textarea>
|
||||
<button class="emoji" @click="emoji" ref="emoji">
|
||||
<fa :icon="['far', 'laugh']"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="files" v-show="files.length != 0">
|
||||
<x-draggable :list="files" :options="{ animation: 150 }">
|
||||
<div v-for="file in files" :key="file.id">
|
||||
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
|
||||
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
|
||||
</div>
|
||||
</x-draggable>
|
||||
</div>
|
||||
<input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
|
||||
<mk-uploader ref="uploader" @uploaded="attachMedia"/>
|
||||
<footer>
|
||||
<button @click="chooseFile"><fa icon="upload"/></button>
|
||||
<button @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
||||
<button @click="post" :disabled="posting" class="post">{{ $t('note') }}</button>
|
||||
</footer>
|
||||
</div>
|
||||
</mk-widget-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import define from '../../../common/define-widget';
|
||||
import i18n from '../../../i18n';
|
||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
import * as XDraggable from 'vuedraggable';
|
||||
|
||||
export default define({
|
||||
name: 'post-form',
|
||||
@@ -19,12 +51,19 @@ export default define({
|
||||
})
|
||||
}).extend({
|
||||
i18n: i18n('desktop/views/widgets/post-form.vue'),
|
||||
|
||||
components: {
|
||||
XDraggable
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
posting: false,
|
||||
text: ''
|
||||
text: '',
|
||||
files: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
placeholder(): string {
|
||||
const xs = [
|
||||
@@ -38,6 +77,7 @@ export default define({
|
||||
return xs[Math.floor(Math.random() * xs.length)];
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
func() {
|
||||
if (this.props.design == 1) {
|
||||
@@ -47,14 +87,68 @@ export default define({
|
||||
}
|
||||
this.save();
|
||||
},
|
||||
|
||||
chooseFile() {
|
||||
(this.$refs.file as any).click();
|
||||
},
|
||||
|
||||
chooseFileFromDrive() {
|
||||
this.$chooseDriveFile({
|
||||
multiple: true
|
||||
}).then(files => {
|
||||
files.forEach(this.attachMedia);
|
||||
});
|
||||
},
|
||||
|
||||
attachMedia(driveFile) {
|
||||
this.files.push(driveFile);
|
||||
this.$emit('change-attached-files', this.files);
|
||||
},
|
||||
|
||||
detachMedia(id) {
|
||||
this.files = this.files.filter(x => x.id != id);
|
||||
this.$emit('change-attached-files', this.files);
|
||||
},
|
||||
|
||||
onKeydown(e) {
|
||||
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && !this.posting && this.text) this.post();
|
||||
},
|
||||
|
||||
onPaste(e) {
|
||||
Array.from(e.clipboardData.items).forEach((item: any) => {
|
||||
if (item.kind == 'file') {
|
||||
this.upload(item.getAsFile());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onChangeFile() {
|
||||
Array.from((this.$refs.file as any).files).forEach(this.upload);
|
||||
},
|
||||
|
||||
upload(file) {
|
||||
(this.$refs.uploader as any).upload(file);
|
||||
},
|
||||
|
||||
async emoji() {
|
||||
const Picker = await import('../components/emoji-picker-dialog.vue').then(m => m.default);
|
||||
const button = this.$refs.emoji;
|
||||
const rect = button.getBoundingClientRect();
|
||||
const vm = this.$root.new(Picker, {
|
||||
x: button.offsetWidth + rect.left + window.pageXOffset,
|
||||
y: rect.top + window.pageYOffset
|
||||
});
|
||||
vm.$once('chosen', emoji => {
|
||||
insertTextAtCursor(this.$refs.text, emoji);
|
||||
});
|
||||
},
|
||||
|
||||
post() {
|
||||
this.posting = true;
|
||||
|
||||
this.$root.api('notes/create', {
|
||||
text: this.text
|
||||
text: this.text == '' ? undefined : this.text,
|
||||
fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||
}).then(data => {
|
||||
this.clear();
|
||||
}).catch(err => {
|
||||
@@ -63,66 +157,114 @@ export default define({
|
||||
this.posting = false;
|
||||
});
|
||||
},
|
||||
|
||||
clear() {
|
||||
this.text = '';
|
||||
this.files = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.lhcuptdmcdkfwmipgazeawoiuxpzaclc-body
|
||||
> .textarea
|
||||
> .emoji
|
||||
position absolute
|
||||
top 0
|
||||
right 0
|
||||
padding 10px
|
||||
font-size 18px
|
||||
color var(--text)
|
||||
opacity 0.5
|
||||
|
||||
&:hover
|
||||
color var(--textHighlighted)
|
||||
opacity 1
|
||||
|
||||
.mkw-post-form
|
||||
background #fff
|
||||
overflow hidden
|
||||
border solid 1px rgba(#000, 0.075)
|
||||
border-radius 6px
|
||||
&:active
|
||||
color var(--primary)
|
||||
opacity 1
|
||||
|
||||
> .title
|
||||
z-index 1
|
||||
margin 0
|
||||
padding 0 16px
|
||||
line-height 42px
|
||||
font-size 0.9em
|
||||
font-weight bold
|
||||
color #888
|
||||
box-shadow 0 1px rgba(#000, 0.07)
|
||||
> textarea
|
||||
display block
|
||||
width 100%
|
||||
max-width 100%
|
||||
min-width 100%
|
||||
padding 16px
|
||||
color var(--desktopPostFormTextareaFg)
|
||||
outline none
|
||||
background var(--desktopPostFormTextareaBg)
|
||||
border none
|
||||
border-bottom solid 1px var(--faceDivider)
|
||||
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
&:focus
|
||||
& + .emoji
|
||||
opacity 0.7
|
||||
|
||||
> textarea
|
||||
display block
|
||||
width 100%
|
||||
max-width 100%
|
||||
min-width 100%
|
||||
padding 16px
|
||||
margin-bottom 28px + 16px
|
||||
border none
|
||||
border-bottom solid 1px #eee
|
||||
> .files
|
||||
> div
|
||||
padding 4px
|
||||
|
||||
> button
|
||||
display block
|
||||
position absolute
|
||||
bottom 8px
|
||||
right 8px
|
||||
margin 0
|
||||
padding 0 10px
|
||||
height 28px
|
||||
color var(--primaryForeground)
|
||||
background var(--primary) !important
|
||||
outline none
|
||||
border none
|
||||
border-radius 4px
|
||||
transition background 0.1s ease
|
||||
cursor pointer
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
clear both
|
||||
|
||||
&:hover
|
||||
background var(--primaryLighten10) !important
|
||||
> div
|
||||
float left
|
||||
border solid 4px transparent
|
||||
cursor move
|
||||
|
||||
&:active
|
||||
background var(--primaryDarken10) !important
|
||||
transition background 0s ease
|
||||
&:hover > .remove
|
||||
display block
|
||||
|
||||
> .img
|
||||
width 64px
|
||||
height 64px
|
||||
background-size cover
|
||||
background-position center center
|
||||
|
||||
> .remove
|
||||
display none
|
||||
position absolute
|
||||
top -6px
|
||||
right -6px
|
||||
width 16px
|
||||
height 16px
|
||||
cursor pointer
|
||||
|
||||
> input[type=file]
|
||||
display none
|
||||
|
||||
> footer
|
||||
display flex
|
||||
padding 8px
|
||||
|
||||
> button:not(.post)
|
||||
color var(--text)
|
||||
|
||||
&:hover
|
||||
color var(--textHighlighted)
|
||||
|
||||
> .post
|
||||
display block
|
||||
margin 0 0 0 auto
|
||||
padding 0 10px
|
||||
height 28px
|
||||
color var(--primaryForeground)
|
||||
background var(--primary) !important
|
||||
outline none
|
||||
border none
|
||||
border-radius 4px
|
||||
transition background 0.1s ease
|
||||
cursor pointer
|
||||
|
||||
&:hover
|
||||
background var(--primaryLighten10) !important
|
||||
|
||||
&:active
|
||||
background var(--primaryDarken10) !important
|
||||
transition background 0s ease
|
||||
|
||||
</style>
|
||||
|
@@ -18,6 +18,7 @@
|
||||
secondary: '$secondary',
|
||||
bg: ':darken<8<$secondary',
|
||||
text: '$text',
|
||||
textHighlighted: ':lighten<7<$text',
|
||||
|
||||
scrollbarTrack: ':darken<5<$secondary',
|
||||
scrollbarHandle: ':lighten<5<$secondary',
|
||||
|
@@ -18,6 +18,7 @@
|
||||
secondary: '$secondary',
|
||||
bg: ':darken<8<$secondary',
|
||||
text: '$text',
|
||||
textHighlighted: ':darken<7<$text',
|
||||
|
||||
scrollbarTrack: '#fff',
|
||||
scrollbarHandle: '#00000033',
|
||||
|
Reference in New Issue
Block a user