Merge branch 'develop' into sw-notification-action
This commit is contained in:
@@ -401,7 +401,8 @@ export default defineComponent({
|
||||
z-index: 65535;
|
||||
max-width: 100%;
|
||||
margin-top: calc(1em + 8px);
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
transition: top 0.1s ease, left 0.1s ease;
|
||||
|
||||
> ol {
|
||||
@@ -418,7 +419,8 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
font-size: 0.9em;
|
||||
cursor: default;
|
||||
|
||||
@@ -427,7 +429,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
* {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
@@ -66,7 +66,8 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
.eftoefju {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
|
@@ -342,7 +342,8 @@ export default defineComponent({
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
color: var(--fg);
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
> .ext {
|
||||
opacity: 0.5;
|
||||
|
201
src/client/components/emoji-picker-dialog.vue
Normal file
201
src/client/components/emoji-picker-dialog.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<MkModal ref="modal" :manual-showing="manualShowing" :src="src" @click="$refs.modal.close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')" v-slot="{ showing }">
|
||||
<MkEmojiPicker v-show="showing !== false" :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen" ref="picker"/>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import MkEmojiPicker from '@/components/emoji-picker.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkModal,
|
||||
MkEmojiPicker,
|
||||
},
|
||||
|
||||
props: {
|
||||
manualShowing: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
src: {
|
||||
required: false
|
||||
},
|
||||
showPinned: {
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
asReactionPicker: {
|
||||
required: false
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['done', 'closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
chosen(emoji: any) {
|
||||
this.$emit('done', emoji);
|
||||
this.$refs.modal.close();
|
||||
},
|
||||
|
||||
opening() {
|
||||
this.$refs.picker.reset();
|
||||
this.$refs.picker.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.omfetrab {
|
||||
$pad: 8px;
|
||||
--eachSize: 40px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
contain: content;
|
||||
|
||||
&.big {
|
||||
--eachSize: 44px;
|
||||
}
|
||||
|
||||
&.w1 {
|
||||
width: calc((var(--eachSize) * 5) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.w2 {
|
||||
width: calc((var(--eachSize) * 6) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.w3 {
|
||||
width: calc((var(--eachSize) * 7) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.h1 {
|
||||
--height: calc((var(--eachSize) * 4) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.h2 {
|
||||
--height: calc((var(--eachSize) * 6) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.h3 {
|
||||
--height: calc((var(--eachSize) * 8) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
> .search {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
|
||||
&:not(.filled) {
|
||||
order: 1;
|
||||
z-index: 2;
|
||||
box-shadow: 0px -1px 0 0px var(--divider);
|
||||
}
|
||||
}
|
||||
|
||||
> .emojis {
|
||||
height: var(--height);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .index {
|
||||
min-height: var(--height);
|
||||
position: relative;
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
> .arrow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 16px 0;
|
||||
text-align: center;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
> header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: $pad;
|
||||
|
||||
> button {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
width: var(--eachSize);
|
||||
height: var(--eachSize);
|
||||
border-radius: 4px;
|
||||
|
||||
&:focus {
|
||||
outline: solid 2px var(--focus);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--accent);
|
||||
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
|
||||
}
|
||||
|
||||
> * {
|
||||
font-size: 24px;
|
||||
height: 1.25em;
|
||||
vertical-align: -.25em;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.result {
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.unicode {
|
||||
min-height: 384px;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
min-height: 64px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
197
src/client/components/emoji-picker-window.vue
Normal file
197
src/client/components/emoji-picker-window.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<MkWindow ref="window"
|
||||
:initial-width="null"
|
||||
:initial-height="null"
|
||||
:can-resize="false"
|
||||
:mini="true"
|
||||
:front="true"
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen"/>
|
||||
</MkWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import MkWindow from '@/components/ui/window.vue';
|
||||
import MkEmojiPicker from '@/components/emoji-picker.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkWindow,
|
||||
MkEmojiPicker,
|
||||
},
|
||||
|
||||
props: {
|
||||
src: {
|
||||
required: false
|
||||
},
|
||||
showPinned: {
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
asReactionPicker: {
|
||||
required: false
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['chosen', 'closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
chosen(emoji: any) {
|
||||
this.$emit('chosen', emoji);
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.omfetrab {
|
||||
$pad: 8px;
|
||||
--eachSize: 40px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
contain: content;
|
||||
|
||||
&.big {
|
||||
--eachSize: 44px;
|
||||
}
|
||||
|
||||
&.w1 {
|
||||
width: calc((var(--eachSize) * 5) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.w2 {
|
||||
width: calc((var(--eachSize) * 6) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.w3 {
|
||||
width: calc((var(--eachSize) * 7) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.h1 {
|
||||
--height: calc((var(--eachSize) * 4) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.h2 {
|
||||
--height: calc((var(--eachSize) * 6) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
&.h3 {
|
||||
--height: calc((var(--eachSize) * 8) + (#{$pad} * 2));
|
||||
}
|
||||
|
||||
> .search {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
|
||||
&:not(.filled) {
|
||||
order: 1;
|
||||
z-index: 2;
|
||||
box-shadow: 0px -1px 0 0px var(--divider);
|
||||
}
|
||||
}
|
||||
|
||||
> .emojis {
|
||||
height: var(--height);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .index {
|
||||
min-height: var(--height);
|
||||
position: relative;
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
> .arrow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 16px 0;
|
||||
text-align: center;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
> header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: $pad;
|
||||
|
||||
> button {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
width: var(--eachSize);
|
||||
height: var(--eachSize);
|
||||
border-radius: 4px;
|
||||
|
||||
&:focus {
|
||||
outline: solid 2px var(--focus);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--accent);
|
||||
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
|
||||
}
|
||||
|
||||
> * {
|
||||
font-size: 24px;
|
||||
height: 1.25em;
|
||||
vertical-align: -.25em;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.result {
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.unicode {
|
||||
min-height: 384px;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
min-height: 64px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
52
src/client/components/emoji-picker.section.vue
Normal file
52
src/client/components/emoji-picker.section.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<section>
|
||||
<header class="_acrylic" @click="shown = !shown">
|
||||
<Fa :icon="shown ? faChevronDown : faChevronUp" :key="shown" fixed-width class="toggle"/> <slot></slot> ({{ emojis.length }})
|
||||
</header>
|
||||
<div v-if="shown">
|
||||
<button v-for="emoji in emojis"
|
||||
class="_button"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji"
|
||||
>
|
||||
<MkEmoji :emoji="emoji" :normal="true"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
emojis: {
|
||||
required: true,
|
||||
},
|
||||
initialShown: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['chosen'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
getStaticImageUrl,
|
||||
shown: this.initialShown,
|
||||
faChevronUp, faChevronDown,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
chosen(emoji: any, ev) {
|
||||
this.$parent.chosen(emoji, ev);
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
@@ -1,117 +1,100 @@
|
||||
<template>
|
||||
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
|
||||
<div class="omfetrab _popup" :class="['w' + width, 'h' + height, { big }]">
|
||||
<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()">
|
||||
<div class="emojis" ref="emojis">
|
||||
<section class="result">
|
||||
<div v-if="searchResultCustom.length > 0">
|
||||
<button v-for="emoji in searchResultCustom"
|
||||
<div class="omfetrab _popup" :class="['w' + width, 'h' + height, { big }]">
|
||||
<input ref="search" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()">
|
||||
<div class="emojis" ref="emojis">
|
||||
<section class="result">
|
||||
<div v-if="searchResultCustom.length > 0">
|
||||
<button v-for="emoji in searchResultCustom"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
|
||||
<img v-else :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="searchResultUnicode.length > 0">
|
||||
<button v-for="emoji in searchResultUnicode"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji.name"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji :emoji="emoji.char"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="index" v-if="tab === 'index'">
|
||||
<section v-if="showPinned">
|
||||
<div>
|
||||
<button v-for="emoji in pinned"
|
||||
class="_button"
|
||||
@click="chosen(emoji, $event)"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji :emoji="emoji" :normal="true"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $ts.recentUsed }}</header>
|
||||
<div>
|
||||
<button v-for="emoji in $store.state.recentlyUsedEmojis"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
|
||||
<img v-else :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="searchResultUnicode.length > 0">
|
||||
<button v-for="emoji in searchResultUnicode"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji.name"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji :emoji="emoji.char"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="index">
|
||||
<section v-if="showPinned">
|
||||
<div>
|
||||
<button v-for="emoji in pinned"
|
||||
class="_button"
|
||||
@click="chosen(emoji, $event)"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji :emoji="emoji" :normal="true"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $ts.recentUsed }}</header>
|
||||
<div>
|
||||
<button v-for="emoji in $store.state.recentlyUsedEmojis"
|
||||
class="_button"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji"
|
||||
>
|
||||
<MkEmoji :emoji="emoji" :normal="true"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="arrow"><Fa :icon="faChevronDown"/></div>
|
||||
</div>
|
||||
|
||||
<section v-for="category in customEmojiCategories" :key="'custom:' + category" class="custom">
|
||||
<header class="_acrylic" v-appear="() => visibleCategories[category] = true">{{ category || $ts.other }}</header>
|
||||
<div v-if="visibleCategories[category]">
|
||||
<button v-for="emoji in customEmojis.filter(e => e.category === category)"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji.name"
|
||||
>
|
||||
<img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-for="category in categories" :key="category.name" class="unicode">
|
||||
<header class="_acrylic" v-appear="() => category.isActive = true"><Fa :icon="category.icon" fixed-width/> {{ category.name }}</header>
|
||||
<div v-if="category.isActive">
|
||||
<button v-for="emoji in emojilist.filter(e => e.category === category.name)"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji.name"
|
||||
>
|
||||
<MkEmoji :emoji="emoji.char"/>
|
||||
<MkEmoji :emoji="emoji" :normal="true"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
<header class="_acrylic">{{ $ts.customEmojis }}</header>
|
||||
<XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')">{{ category || $ts.other }}</XSection>
|
||||
</div>
|
||||
<div>
|
||||
<header class="_acrylic">{{ $ts.emoji }}</header>
|
||||
<XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)">{{ category }}</XSection>
|
||||
</div>
|
||||
<div>
|
||||
<header class="_acrylic">{{ $ts.tags }}</header>
|
||||
<XSection v-for="tag in emojiTags" :emojis="customEmojis.filter(e => e.aliases.includes(tag)).map(e => ':' + e.name + ':')">{{ tag }}</XSection>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
<div class="tabs">
|
||||
<button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><Fa :icon="faAsterisk" fixed-width/></button>
|
||||
<button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><Fa :icon="faLaugh" fixed-width/></button>
|
||||
<button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><Fa :icon="faLeaf" fixed-width/></button>
|
||||
<button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><Fa :icon="faHashtag" fixed-width/></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { emojilist } from '../../misc/emojilist';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faClock, faUser, faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faClock, faUser, faChevronDown, faShapes, faBicycle, faHashtag } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import Particle from '@/components/particle.vue';
|
||||
import * as os from '@/os';
|
||||
import { isDeviceTouch } from '@/scripts/is-device-touch';
|
||||
import { isMobile } from '@/scripts/is-mobile';
|
||||
import { emojiCategories } from '@/instance';
|
||||
import { emojiCategories, emojiTags } from '@/instance';
|
||||
import XSection from './emoji-picker.section.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkModal,
|
||||
XSection
|
||||
},
|
||||
|
||||
props: {
|
||||
src: {
|
||||
required: false
|
||||
},
|
||||
showPinned: {
|
||||
required: false,
|
||||
default: true
|
||||
@@ -121,7 +104,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['done', 'closed'],
|
||||
emits: ['chosen'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
@@ -132,50 +115,14 @@ export default defineComponent({
|
||||
height: this.asReactionPicker ? this.$store.state.reactionPickerHeight : 2,
|
||||
big: this.asReactionPicker ? isDeviceTouch : false,
|
||||
customEmojiCategories: emojiCategories,
|
||||
emojiTags,
|
||||
customEmojis: this.$instance.emojis,
|
||||
visibleCategories: {},
|
||||
q: null,
|
||||
searchResultCustom: [],
|
||||
searchResultUnicode: [],
|
||||
faGlobe, faClock, faChevronDown,
|
||||
categories: [{
|
||||
name: 'face',
|
||||
icon: faLaugh,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'people',
|
||||
icon: faUser,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'animals_and_nature',
|
||||
icon: faLeaf,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'food_and_drink',
|
||||
icon: faUtensils,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'activity',
|
||||
icon: faFutbol,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'travel_and_places',
|
||||
icon: faCity,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'objects',
|
||||
icon: faDice,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'symbols',
|
||||
icon: faHeart,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'flags',
|
||||
icon: faFlag,
|
||||
isActive: false
|
||||
}],
|
||||
faAsterisk
|
||||
tab: 'index',
|
||||
categories: ['face', 'people', 'animals_and_nature', 'food_and_drink', 'activity', 'travel_and_places', 'objects', 'symbols', 'flags'],
|
||||
faGlobe, faClock, faChevronDown, faAsterisk, faLaugh, faUtensils, faLeaf, faShapes, faBicycle, faHashtag,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -323,14 +270,22 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (!isMobile && !isDeviceTouch) {
|
||||
this.$refs.search.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
this.focus();
|
||||
},
|
||||
|
||||
methods: {
|
||||
focus() {
|
||||
if (!isMobile && !isDeviceTouch) {
|
||||
this.$refs.search.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.$refs.emojis.scrollTop = 0;
|
||||
},
|
||||
|
||||
getKey(emoji: any) {
|
||||
return typeof emoji === 'string' ? emoji : (emoji.char || `:${emoji.name}:`);
|
||||
},
|
||||
@@ -345,15 +300,14 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const key = this.getKey(emoji);
|
||||
this.$emit('done', key);
|
||||
this.$refs.modal.close();
|
||||
this.$emit('chosen', key);
|
||||
|
||||
// 最近使った絵文字更新
|
||||
if (!this.pinned.includes(key)) {
|
||||
let recents = this.$store.state.recentlyUsedEmojis;
|
||||
recents = recents.filter((e: any) => e !== key);
|
||||
recents.unshift(key);
|
||||
this.$store.set('recentlyUsedEmojis', recents.splice(0, 16));
|
||||
this.$store.set('recentlyUsedEmojis', recents.splice(0, 32));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -445,6 +399,22 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
> .tabs {
|
||||
display: flex;
|
||||
display: none;
|
||||
|
||||
> .tab {
|
||||
flex: 1;
|
||||
height: 38px;
|
||||
border-top: solid 1px var(--divider);
|
||||
|
||||
&.active {
|
||||
border-top: solid 1px var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .emojis {
|
||||
height: var(--height);
|
||||
overflow-y: auto;
|
||||
@@ -456,34 +426,43 @@ export default defineComponent({
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .index {
|
||||
min-height: var(--height);
|
||||
position: relative;
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
> .arrow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 16px 0;
|
||||
text-align: center;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
> div {
|
||||
&:not(.index) {
|
||||
padding: 4px 0 8px 0;
|
||||
border-top: solid 1px var(--divider);
|
||||
}
|
||||
|
||||
> header {
|
||||
/*position: sticky;
|
||||
top: 0;
|
||||
left: 0;*/
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
z-index: 2;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
::v-deep(section) {
|
||||
> header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
z-index: 1;
|
||||
padding: 8px;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
position: relative;
|
||||
padding: $pad;
|
||||
|
||||
> button {
|
||||
@@ -523,14 +502,6 @@ export default defineComponent({
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.unicode {
|
||||
min-height: 384px;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
min-height: 64px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -261,7 +261,8 @@ export default defineComponent({
|
||||
display: inline-block;
|
||||
min-width: 16px;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@@ -83,7 +83,8 @@ export default defineComponent({
|
||||
> .text {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
|
@@ -101,7 +101,8 @@ export default defineComponent({
|
||||
top: 0;
|
||||
border-radius: 100%;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@@ -126,7 +126,8 @@ export default defineComponent({
|
||||
&.nowrap {
|
||||
white-space: pre;
|
||||
word-wrap: normal; // https://codeday.me/jp/qa/20190424/690106.html
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
@@ -692,7 +692,8 @@ export default defineComponent({
|
||||
|
||||
> dd {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
@@ -44,7 +44,8 @@ export default defineComponent({
|
||||
|
||||
height: $height;
|
||||
border-radius: 4px 0 0 4px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
color: #fff;
|
||||
|
||||
> .icon {
|
||||
|
@@ -62,7 +62,8 @@ export default defineComponent({
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
> .download,
|
||||
> .sensitive {
|
||||
@@ -77,7 +78,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> b {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
@@ -143,7 +143,8 @@ export default defineComponent({
|
||||
> a {
|
||||
display: block;
|
||||
cursor: zoom-in;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
|
@@ -105,7 +105,8 @@ export default defineComponent({
|
||||
grid-gap: 4px;
|
||||
|
||||
> * {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
|
@@ -78,7 +78,8 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
|
||||
font-size: 3.5em;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
|
@@ -523,20 +523,14 @@ export default defineComponent({
|
||||
react(viaKeyboard = false) {
|
||||
pleaseLogin();
|
||||
this.blur();
|
||||
os.popup(import('@/components/emoji-picker.vue'), {
|
||||
src: this.$refs.reactButton,
|
||||
asReactionPicker: true
|
||||
}, {
|
||||
done: reaction => {
|
||||
if (reaction) {
|
||||
os.api('notes/reactions/create', {
|
||||
noteId: this.appearNote.id,
|
||||
reaction: reaction
|
||||
});
|
||||
}
|
||||
this.focus();
|
||||
},
|
||||
}, 'closed');
|
||||
os.pickReaction(this.$refs.reactButton, reaction => {
|
||||
os.api('notes/reactions/create', {
|
||||
noteId: this.appearNote.id,
|
||||
reaction: reaction
|
||||
});
|
||||
}, () => {
|
||||
this.focus();
|
||||
});
|
||||
},
|
||||
|
||||
reactDirectly(reaction) {
|
||||
@@ -892,7 +886,8 @@ export default defineComponent({
|
||||
.note {
|
||||
position: relative;
|
||||
transition: box-shadow 0.1s ease;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
contain: content;
|
||||
|
||||
&:focus-visible {
|
||||
@@ -952,7 +947,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> span {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
flex-shrink: 1;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
@@ -61,7 +61,8 @@ export default defineComponent({
|
||||
display: block;
|
||||
margin: 0 .5em 0 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
@@ -90,7 +91,8 @@ export default defineComponent({
|
||||
|
||||
> .username {
|
||||
margin: 0 .5em 0 0;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
@@ -50,7 +50,8 @@ export default defineComponent({
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
font-size: 0.95em;
|
||||
|
||||
> .avatar {
|
||||
|
@@ -498,20 +498,14 @@ export default defineComponent({
|
||||
react(viaKeyboard = false) {
|
||||
pleaseLogin();
|
||||
this.blur();
|
||||
os.popup(import('@/components/emoji-picker.vue'), {
|
||||
src: this.$refs.reactButton,
|
||||
asReactionPicker: true
|
||||
}, {
|
||||
done: reaction => {
|
||||
if (reaction) {
|
||||
os.api('notes/reactions/create', {
|
||||
noteId: this.appearNote.id,
|
||||
reaction: reaction
|
||||
});
|
||||
}
|
||||
this.focus();
|
||||
},
|
||||
}, 'closed');
|
||||
os.pickReaction(this.$refs.reactButton, reaction => {
|
||||
os.api('notes/reactions/create', {
|
||||
noteId: this.appearNote.id,
|
||||
reaction: reaction
|
||||
});
|
||||
}, () => {
|
||||
this.focus();
|
||||
});
|
||||
},
|
||||
|
||||
reactDirectly(reaction) {
|
||||
@@ -867,7 +861,8 @@ export default defineComponent({
|
||||
.tkcbzcuz {
|
||||
position: relative;
|
||||
transition: box-shadow 0.1s ease;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
contain: content;
|
||||
|
||||
// これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、
|
||||
@@ -954,7 +949,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> span {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
flex-shrink: 1;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -1026,7 +1022,8 @@ export default defineComponent({
|
||||
&.collapsed {
|
||||
position: relative;
|
||||
max-height: 9em;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
> .fade {
|
||||
display: block;
|
||||
|
@@ -268,7 +268,8 @@ export default defineComponent({
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
> .time {
|
||||
@@ -279,7 +280,8 @@ export default defineComponent({
|
||||
|
||||
> .text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
> [data-icon] {
|
||||
|
@@ -112,7 +112,8 @@ export default defineComponent({
|
||||
padding: 4px 8px;
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
|
@@ -127,7 +127,8 @@ export default defineComponent({
|
||||
height: 64px;
|
||||
margin-right: 4px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
cursor: move;
|
||||
|
||||
&:hover > .remove {
|
||||
|
@@ -632,9 +632,7 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
async insertEmoji(ev) {
|
||||
os.pickEmoji(ev.currentTarget || ev.target).then(emoji => {
|
||||
insertTextAtCursor(this.$refs.text, emoji);
|
||||
});
|
||||
os.openEmojiPicker(ev.currentTarget || ev.target, {}, this.$refs.text);
|
||||
},
|
||||
|
||||
showActions(ev) {
|
||||
|
@@ -390,7 +390,8 @@ export default defineComponent({
|
||||
font-size: $ui-font-size;
|
||||
line-height: 3rem;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
@@ -67,7 +67,8 @@ export default defineComponent({
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -124,7 +124,8 @@ export default defineComponent({
|
||||
text-decoration: none;
|
||||
background: var(--buttonBg);
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background: var(--buttonHoverBg);
|
||||
@@ -212,7 +213,8 @@ export default defineComponent({
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
::v-deep(div) {
|
||||
position: absolute;
|
||||
|
@@ -116,7 +116,8 @@ export default defineComponent({
|
||||
|
||||
.ukygtjoj {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
&.naked {
|
||||
background: transparent !important;
|
||||
|
@@ -298,7 +298,8 @@ export default defineComponent({
|
||||
transform: scale(.75);
|
||||
white-space: nowrap;
|
||||
width: 133%;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
> .warning {
|
||||
@@ -354,7 +355,8 @@ export default defineComponent({
|
||||
display: inline-block;
|
||||
min-width: 16px;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@@ -155,7 +155,8 @@ export default defineComponent({
|
||||
font-size: 0.9em;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&.danger {
|
||||
|
@@ -89,7 +89,8 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ebkgoccj {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
contain: content;
|
||||
@@ -123,7 +124,8 @@ export default defineComponent({
|
||||
padding-left: 32px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
pointer-events: none;
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: showing ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
|
||||
<div class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
|
||||
<transition :name="$store.state.animation ? 'modal-bg' : ''" appear>
|
||||
<div class="bg _modalBg" v-if="showing" @click="onBgClick"></div>
|
||||
<div class="bg _modalBg" v-if="manualShowing != null ? manualShowing : showing" @click="onBgClick"></div>
|
||||
</transition>
|
||||
<div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content">
|
||||
<transition :name="$store.state.animation ? popup ? 'modal-popup-content' : 'modal-content' : ''" appear @after-leave="$emit('closed')" @after-enter="childRendered">
|
||||
<slot v-if="showing"></slot>
|
||||
<transition :name="$store.state.animation ? popup ? 'modal-popup-content' : 'modal-content' : ''" appear @after-leave="$emit('closed')" @enter="$emit('opening')" @after-enter="childRendered">
|
||||
<slot v-if="manualShowing != null ? true : showing" v-bind:showing="manualShowing"></slot>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
@@ -29,6 +29,11 @@ export default defineComponent({
|
||||
modal: true
|
||||
},
|
||||
props: {
|
||||
manualShowing: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
srcCenter: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
@@ -40,7 +45,7 @@ export default defineComponent({
|
||||
required: false
|
||||
}
|
||||
},
|
||||
emits: ['click', 'esc', 'closed'],
|
||||
emits: ['opening', 'click', 'esc', 'close', 'closed'],
|
||||
data() {
|
||||
return {
|
||||
showing: true,
|
||||
@@ -60,71 +65,82 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fixed = getFixedContainer(this.src) != null;
|
||||
this.$watch('src', () => {
|
||||
this.fixed = getFixedContainer(this.src) != null;
|
||||
this.$nextTick(() => {
|
||||
this.align();
|
||||
});
|
||||
}, { immediate: true });
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (!this.popup) return;
|
||||
|
||||
const popover = this.$refs.content as any;
|
||||
|
||||
// TODO: ResizeObserver無くしたい
|
||||
new ResizeObserver((entries, observer) => {
|
||||
const rect = this.src.getBoundingClientRect();
|
||||
|
||||
const width = popover.offsetWidth;
|
||||
const height = popover.offsetHeight;
|
||||
|
||||
let left;
|
||||
let top;
|
||||
|
||||
if (this.srcCenter) {
|
||||
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
|
||||
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + (this.src.offsetHeight / 2);
|
||||
left = (x - (width / 2));
|
||||
top = (y - (height / 2));
|
||||
} else {
|
||||
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
|
||||
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + this.src.offsetHeight;
|
||||
left = (x - (width / 2));
|
||||
top = y;
|
||||
}
|
||||
|
||||
if (this.fixed) {
|
||||
if (left + width > window.innerWidth) {
|
||||
left = window.innerWidth - width;
|
||||
}
|
||||
|
||||
if (top + height > window.innerHeight) {
|
||||
top = window.innerHeight - height;
|
||||
}
|
||||
} else {
|
||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
||||
left = window.innerWidth - width + window.pageXOffset - 1;
|
||||
}
|
||||
|
||||
if (top + height - window.pageYOffset > window.innerHeight) {
|
||||
top = window.innerHeight - height + window.pageYOffset - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
}
|
||||
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
if (top > rect.top + (this.fixed ? 0 : window.pageYOffset)) {
|
||||
this.transformOrigin = 'center top';
|
||||
}
|
||||
|
||||
popover.style.left = left + 'px';
|
||||
popover.style.top = top + 'px';
|
||||
this.align();
|
||||
}).observe(popover);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
align() {
|
||||
if (!this.popup) return;
|
||||
|
||||
const popover = this.$refs.content as any;
|
||||
|
||||
const rect = this.src.getBoundingClientRect();
|
||||
|
||||
const width = popover.offsetWidth;
|
||||
const height = popover.offsetHeight;
|
||||
|
||||
let left;
|
||||
let top;
|
||||
|
||||
if (this.srcCenter) {
|
||||
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
|
||||
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + (this.src.offsetHeight / 2);
|
||||
left = (x - (width / 2));
|
||||
top = (y - (height / 2));
|
||||
} else {
|
||||
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
|
||||
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + this.src.offsetHeight;
|
||||
left = (x - (width / 2));
|
||||
top = y;
|
||||
}
|
||||
|
||||
if (this.fixed) {
|
||||
if (left + width > window.innerWidth) {
|
||||
left = window.innerWidth - width;
|
||||
}
|
||||
|
||||
if (top + height > window.innerHeight) {
|
||||
top = window.innerHeight - height;
|
||||
}
|
||||
} else {
|
||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
||||
left = window.innerWidth - width + window.pageXOffset - 1;
|
||||
}
|
||||
|
||||
if (top + height - window.pageYOffset > window.innerHeight) {
|
||||
top = window.innerHeight - height + window.pageYOffset - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
}
|
||||
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
if (top > rect.top + (this.fixed ? 0 : window.pageYOffset)) {
|
||||
this.transformOrigin = 'center top';
|
||||
} else {
|
||||
this.transformOrigin = 'center';
|
||||
}
|
||||
|
||||
popover.style.left = left + 'px';
|
||||
popover.style.top = top + 'px';
|
||||
},
|
||||
|
||||
childRendered() {
|
||||
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
||||
const content = this.$refs.content.children[0];
|
||||
@@ -141,6 +157,7 @@ export default defineComponent({
|
||||
|
||||
close() {
|
||||
this.showing = false;
|
||||
this.$emit('close');
|
||||
},
|
||||
|
||||
onBgClick() {
|
||||
|
@@ -89,7 +89,8 @@ export default defineComponent({
|
||||
pointer-events: none;
|
||||
font-size: 16px;
|
||||
color: var(--inputLabel);
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
> input {
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<transition :name="$store.state.animation ? 'window' : ''" appear @after-leave="$emit('closed')">
|
||||
<div class="ebkgocck" v-if="showing">
|
||||
<div class="ebkgocck" :class="{ front }" v-if="showing">
|
||||
<div class="body _popup _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown">
|
||||
<div class="header" @contextmenu.prevent.stop="onContextmenu">
|
||||
<div class="header" :class="{ mini }" @contextmenu.prevent.stop="onContextmenu">
|
||||
<slot v-if="closeRight" name="buttons"><button class="_button" style="pointer-events: none;"></button></slot>
|
||||
<button v-else class="_button" @click="close()"><Fa :icon="faTimes"/></button>
|
||||
|
||||
@@ -92,6 +92,16 @@ export default defineComponent({
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
mini: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
front: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
contextmenu: {
|
||||
type: Array,
|
||||
required: false,
|
||||
@@ -387,8 +397,13 @@ export default defineComponent({
|
||||
left: 0;
|
||||
z-index: 5000;
|
||||
|
||||
&.front {
|
||||
z-index: 11000; // front指定の時は、mk-modalのよりも大きくなければならない
|
||||
}
|
||||
|
||||
> .body {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
contain: content;
|
||||
@@ -396,17 +411,22 @@ export default defineComponent({
|
||||
height: 100%;
|
||||
|
||||
> .header {
|
||||
$height: 50px;
|
||||
--height: 50px;
|
||||
|
||||
&.mini {
|
||||
--height: 38px;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
height: $height;
|
||||
height: var(--height);
|
||||
|
||||
> ::v-deep(button) {
|
||||
height: $height;
|
||||
width: $height;
|
||||
height: var(--height);
|
||||
width: var(--height);
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
@@ -416,9 +436,10 @@ export default defineComponent({
|
||||
> .title {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
line-height: $height;
|
||||
line-height: var(--height);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
cursor: move;
|
||||
|
@@ -244,7 +244,8 @@ export default defineComponent({
|
||||
font-size: 14px;
|
||||
box-shadow: 0 0 0 1px var(--divider);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
@@ -326,7 +327,8 @@ export default defineComponent({
|
||||
&.compact {
|
||||
> article {
|
||||
> header h1, p, footer {
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@@ -110,7 +110,8 @@ export default defineComponent({
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -111,7 +111,8 @@ export default defineComponent({
|
||||
position: absolute;
|
||||
z-index: 11000;
|
||||
width: 300px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
transform-origin: center top;
|
||||
|
||||
> .info {
|
||||
|
@@ -66,7 +66,8 @@ export default defineComponent({
|
||||
height: 350px;
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -127,7 +128,8 @@ export default defineComponent({
|
||||
|
||||
> .body {
|
||||
padding: 0 8px;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
|
||||
> .name {
|
||||
display: block;
|
||||
|
@@ -141,7 +141,8 @@ export default defineComponent({
|
||||
> *:nth-child(2) {
|
||||
flex: 1 1 auto;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
overflow: hidden; // overflow: clip; をSafariが対応したら消す
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
> span:first-child {
|
||||
|
Reference in New Issue
Block a user