Merge branch 'develop' into swn
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<canvas ref="chartEl"></canvas>
|
||||
<div class="cbbedffa">
|
||||
<canvas ref="chartEl"></canvas>
|
||||
<div v-if="fetching" class="fetching">
|
||||
<MkLoading/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -84,6 +89,16 @@ export default defineComponent({
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
stacked: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
aspectRatio: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
@@ -152,7 +167,7 @@ export default defineComponent({
|
||||
})),
|
||||
},
|
||||
options: {
|
||||
aspectRatio: 2.5,
|
||||
aspectRatio: props.aspectRatio || 2.5,
|
||||
layout: {
|
||||
padding: {
|
||||
left: 16,
|
||||
@@ -169,7 +184,6 @@ export default defineComponent({
|
||||
unit: props.span === 'day' ? 'month' : 'day',
|
||||
},
|
||||
grid: {
|
||||
display: props.detailed,
|
||||
color: gridColor,
|
||||
borderColor: 'rgb(0, 0, 0, 0)',
|
||||
},
|
||||
@@ -185,6 +199,7 @@ export default defineComponent({
|
||||
},
|
||||
y: {
|
||||
position: 'left',
|
||||
stacked: props.stacked,
|
||||
grid: {
|
||||
color: gridColor,
|
||||
borderColor: 'rgb(0, 0, 0, 0)',
|
||||
@@ -199,6 +214,7 @@ export default defineComponent({
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: props.detailed,
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
boxWidth: 16,
|
||||
@@ -578,6 +594,30 @@ export default defineComponent({
|
||||
};
|
||||
};
|
||||
|
||||
const fetchPerUserNotesChart = async (): Promise<typeof data> => {
|
||||
const raw = await os.api('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span });
|
||||
return {
|
||||
series: [...(props.args.withoutAll ? [] : [{
|
||||
name: 'All',
|
||||
type: 'line',
|
||||
borderDash: [5, 5],
|
||||
data: format(sum(raw.inc, negate(raw.dec))),
|
||||
}]), {
|
||||
name: 'Renotes',
|
||||
type: 'area',
|
||||
data: format(raw.diffs.renote),
|
||||
}, {
|
||||
name: 'Replies',
|
||||
type: 'area',
|
||||
data: format(raw.diffs.reply),
|
||||
}, {
|
||||
name: 'Normal',
|
||||
type: 'area',
|
||||
data: format(raw.diffs.normal),
|
||||
}],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchAndRender = async () => {
|
||||
const fetchData = () => {
|
||||
switch (props.src) {
|
||||
@@ -606,6 +646,8 @@ export default defineComponent({
|
||||
case 'instance-drive-usage-total': return fetchInstanceDriveUsageChart(true);
|
||||
case 'instance-drive-files': return fetchInstanceDriveFilesChart(false);
|
||||
case 'instance-drive-files-total': return fetchInstanceDriveFilesChart(true);
|
||||
|
||||
case 'per-user-notes': return fetchPerUserNotesChart();
|
||||
}
|
||||
};
|
||||
fetching.value = true;
|
||||
@@ -622,7 +664,28 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
chartEl,
|
||||
fetching,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cbbedffa {
|
||||
position: relative;
|
||||
|
||||
> .fetching {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-backdrop-filter: var(--blur, blur(12px));
|
||||
backdrop-filter: var(--blur, blur(12px));
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: wait;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -24,7 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
|
||||
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode } from 'vue';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
import * as os from '@client/os';
|
||||
|
||||
@@ -140,6 +140,16 @@ export default defineComponent({
|
||||
const menu = [];
|
||||
let options = context.slots.default();
|
||||
|
||||
const pushOption = (option: VNode) => {
|
||||
menu.push({
|
||||
text: option.children,
|
||||
active: v.value === option.props.value,
|
||||
action: () => {
|
||||
v.value = option.props.value;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
for (const optionOrOptgroup of options) {
|
||||
if (optionOrOptgroup.type === 'optgroup') {
|
||||
const optgroup = optionOrOptgroup;
|
||||
@@ -148,23 +158,16 @@ export default defineComponent({
|
||||
text: optgroup.props.label,
|
||||
});
|
||||
for (const option of optgroup.children) {
|
||||
menu.push({
|
||||
text: option.children,
|
||||
active: v.value === option.props.value,
|
||||
action: () => {
|
||||
v.value = option.props.value;
|
||||
},
|
||||
});
|
||||
pushOption(option);
|
||||
}
|
||||
} else if (Array.isArray(optionOrOptgroup.children)) { // 何故かフラグメントになってくることがある
|
||||
const fragment = optionOrOptgroup;
|
||||
for (const option of fragment.children) {
|
||||
pushOption(option);
|
||||
}
|
||||
} else {
|
||||
const option = optionOrOptgroup;
|
||||
menu.push({
|
||||
text: option.children,
|
||||
active: v.value === option.props.value,
|
||||
action: () => {
|
||||
v.value = option.props.value;
|
||||
},
|
||||
});
|
||||
pushOption(option);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,8 +2,8 @@
|
||||
<div class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick" ref="el">
|
||||
<template v-if="info">
|
||||
<div class="titleContainer" @click="showTabsPopup" v-if="!hideTitle">
|
||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/>
|
||||
<MkAvatar v-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/>
|
||||
<i v-else-if="info.icon" class="icon" :class="info.icon"></i>
|
||||
|
||||
<div class="title">
|
||||
<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/>
|
||||
@@ -162,11 +162,6 @@ export default defineComponent({
|
||||
onUnmounted(() => {
|
||||
ro.disconnect();
|
||||
});
|
||||
setTimeout(() => {
|
||||
const currentStickyTop = getComputedStyle(el.value.parentElement).getPropertyValue('--stickyTop') || '0px';
|
||||
el.value.style.setProperty('--stickyTop', currentStickyTop);
|
||||
el.value.parentElement.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${el.value.offsetHeight}px)`);
|
||||
}, 100); // レンダリング順序の関係で親のstickyTopの設定が少し遅れることがあるため
|
||||
}
|
||||
});
|
||||
|
||||
|
74
src/client/components/global/sticky-container.vue
Normal file
74
src/client/components/global/sticky-container.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div ref="rootEl">
|
||||
<slot name="header"></slot>
|
||||
<div ref="bodyEl">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
autoSticky: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
const rootEl = ref<HTMLElement>(null);
|
||||
const bodyEl = ref<HTMLElement>(null);
|
||||
|
||||
const calc = () => {
|
||||
const currentStickyTop = getComputedStyle(rootEl.value).getPropertyValue('--stickyTop') || '0px';
|
||||
|
||||
const header = rootEl.value.children[0];
|
||||
if (header === bodyEl.value) {
|
||||
bodyEl.value.style.setProperty('--stickyTop', currentStickyTop);
|
||||
} else {
|
||||
bodyEl.value.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${header.offsetHeight}px)`);
|
||||
|
||||
if (props.autoSticky) {
|
||||
header.style.setProperty('--stickyTop', currentStickyTop);
|
||||
header.style.position = 'sticky';
|
||||
header.style.top = 'var(--stickyTop)';
|
||||
header.style.zIndex = '1';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
calc();
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
setTimeout(() => {
|
||||
calc();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
observer.observe(rootEl.value, {
|
||||
attributes: false,
|
||||
childList: true,
|
||||
subtree: false,
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
observer.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
rootEl,
|
||||
bodyEl,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
</style>
|
@@ -15,6 +15,7 @@ import error from './global/error.vue';
|
||||
import ad from './global/ad.vue';
|
||||
import header from './global/header.vue';
|
||||
import spacer from './global/spacer.vue';
|
||||
import stickyContainer from './global/sticky-container.vue';
|
||||
|
||||
export default function(app: App) {
|
||||
app.component('I18n', i18n);
|
||||
@@ -32,4 +33,5 @@ export default function(app: App) {
|
||||
app.component('MkAd', ad);
|
||||
app.component('MkHeader', header);
|
||||
app.component('MkSpacer', spacer);
|
||||
app.component('MkStickyContainer', stickyContainer);
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<MkA class="ldlomzub" :class="{ isMe }" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
|
||||
<span class="me" v-if="isMe">{{ $ts.you }}</span>
|
||||
<img class="icon" :src="`/avatar/@${username}@${host}`" alt="">
|
||||
<span class="main">
|
||||
<span class="username">@{{ username }}</span>
|
||||
<span class="host" v-if="(host != localHost) || $store.state.showFullAcct">@{{ toUnicode(host) }}</span>
|
||||
@@ -76,6 +77,13 @@ export default defineComponent({
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
> .icon {
|
||||
width: 1.5em;
|
||||
margin: 0 0.2em;
|
||||
vertical-align: bottom;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
> .main {
|
||||
> .host {
|
||||
opacity: 0.5;
|
||||
|
@@ -10,10 +10,13 @@
|
||||
</span>
|
||||
<button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<div class="body _fitSide_">
|
||||
<keep-alive>
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
</keep-alive>
|
||||
<div class="body">
|
||||
<MkStickyContainer>
|
||||
<template #header><MkHeader v-if="pageInfo && !pageInfo.hideHeader" :info="pageInfo"/></template>
|
||||
<keep-alive>
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
</keep-alive>
|
||||
</MkStickyContainer>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
|
@@ -16,8 +16,11 @@
|
||||
<template #headerLeft>
|
||||
<button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button>
|
||||
</template>
|
||||
<div class="yrolvcoq _fitSide_">
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
<div class="yrolvcoq">
|
||||
<MkStickyContainer>
|
||||
<template #header><MkHeader v-if="pageInfo && !pageInfo.hideHeader" :info="pageInfo"/></template>
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
</MkStickyContainer>
|
||||
</div>
|
||||
</XWindow>
|
||||
</template>
|
||||
|
@@ -79,6 +79,7 @@ export default defineComponent({
|
||||
pointRadius: 0,
|
||||
tension: 0,
|
||||
borderWidth: 2,
|
||||
borderJoinStyle: 'round',
|
||||
borderColor: '#00E396',
|
||||
backgroundColor: alpha('#00E396', 0.1),
|
||||
data: []
|
||||
@@ -87,6 +88,7 @@ export default defineComponent({
|
||||
pointRadius: 0,
|
||||
tension: 0,
|
||||
borderWidth: 2,
|
||||
borderJoinStyle: 'round',
|
||||
borderColor: '#00BCD4',
|
||||
backgroundColor: alpha('#00BCD4', 0.1),
|
||||
data: []
|
||||
@@ -95,17 +97,21 @@ export default defineComponent({
|
||||
pointRadius: 0,
|
||||
tension: 0,
|
||||
borderWidth: 2,
|
||||
borderJoinStyle: 'round',
|
||||
borderColor: '#FFB300',
|
||||
backgroundColor: alpha('#FFB300', 0.1),
|
||||
yAxisID: 'y2',
|
||||
data: []
|
||||
}, {
|
||||
label: 'Delayed',
|
||||
pointRadius: 0,
|
||||
tension: 0,
|
||||
borderWidth: 2,
|
||||
borderJoinStyle: 'round',
|
||||
borderColor: '#E53935',
|
||||
borderDash: [5, 5],
|
||||
fill: false,
|
||||
yAxisID: 'y2',
|
||||
data: []
|
||||
}],
|
||||
},
|
||||
@@ -122,15 +128,29 @@ export default defineComponent({
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false,
|
||||
display: true,
|
||||
color: gridColor,
|
||||
borderColor: 'rgb(0, 0, 0, 0)',
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
maxTicksLimit: 10
|
||||
},
|
||||
},
|
||||
y: {
|
||||
min: 0,
|
||||
stack: 'queue',
|
||||
stackWeight: 2,
|
||||
grid: {
|
||||
color: gridColor,
|
||||
borderColor: 'rgb(0, 0, 0, 0)',
|
||||
},
|
||||
},
|
||||
y2: {
|
||||
min: 0,
|
||||
offset: true,
|
||||
stack: 'queue',
|
||||
stackWeight: 1,
|
||||
grid: {
|
||||
color: gridColor,
|
||||
borderColor: 'rgb(0, 0, 0, 0)',
|
||||
|
@@ -1,23 +1,25 @@
|
||||
<template>
|
||||
<MkTooltip :source="source" ref="tooltip" @closed="$emit('closed')">
|
||||
<MkTooltip :source="source" ref="tooltip" @closed="$emit('closed')" :max-width="340">
|
||||
<div class="bqxuuuey">
|
||||
<div class="info">
|
||||
<div>{{ reaction.replace('@.', '') }}</div>
|
||||
<div class="reaction">
|
||||
<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon" :no-style="true"/>
|
||||
<div class="name">{{ reaction.replace('@.', '') }}</div>
|
||||
</div>
|
||||
<div class="users">
|
||||
<template v-if="users.length <= 10">
|
||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
||||
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
||||
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
|
||||
</b>
|
||||
</template>
|
||||
<template v-if="10 < users.length">
|
||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
||||
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
||||
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
|
||||
</b>
|
||||
<span slot="omitted">+{{ count - 10 }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<template v-if="users.length <= 10">
|
||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
||||
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
||||
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
|
||||
</b>
|
||||
</template>
|
||||
<template v-if="10 < users.length">
|
||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
||||
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
||||
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
|
||||
</b>
|
||||
<span slot="omitted">+{{ count - 10 }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</MkTooltip>
|
||||
</template>
|
||||
@@ -59,8 +61,11 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bqxuuuey {
|
||||
> .info {
|
||||
padding: 0 0 8px 0;
|
||||
display: flex;
|
||||
|
||||
> .reaction {
|
||||
flex: 1;
|
||||
max-width: 100px;
|
||||
text-align: center;
|
||||
|
||||
> .icon {
|
||||
@@ -68,6 +73,19 @@ export default defineComponent({
|
||||
width: 60px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
> .name {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
> .users {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 0.9em;
|
||||
border-left: solid 0.5px var(--divider);
|
||||
padding-left: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -177,6 +177,7 @@ export default defineComponent({
|
||||
> span {
|
||||
font-size: 0.9em;
|
||||
line-height: 32px;
|
||||
margin: 0 0 0 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -153,10 +153,4 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
._fitSide_ .ssazuxis {
|
||||
> header {
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -42,8 +42,4 @@ export default defineComponent({
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
._fitSide_ .fpezltsf {
|
||||
border-radius: 0;
|
||||
}
|
||||
</style>
|
||||
|
@@ -50,9 +50,6 @@ export default defineComponent({
|
||||
border-top: solid 0.5px var(--divider);
|
||||
}
|
||||
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
|
||||
> .title {
|
||||
font-size: 0.9em;
|
||||
opacity: 0.7;
|
||||
@@ -120,7 +117,7 @@ export default defineComponent({
|
||||
|
||||
> .items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
|
||||
grid-gap: 8px;
|
||||
padding: 0 16px;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<transition name="tooltip" appear @after-leave="$emit('closed')">
|
||||
<div class="buebdbiu _acrylic _shadow" v-show="showing" ref="content">
|
||||
<div class="buebdbiu _acrylic _shadow" v-show="showing" ref="content" :style="{ maxWidth: maxWidth + 'px' }">
|
||||
<slot>{{ text }}</slot>
|
||||
</div>
|
||||
</transition>
|
||||
@@ -21,7 +21,12 @@ export default defineComponent({
|
||||
text: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
maxWidth: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 250,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['closed'],
|
||||
@@ -75,11 +80,12 @@ export default defineComponent({
|
||||
.buebdbiu {
|
||||
position: absolute;
|
||||
z-index: 11000;
|
||||
max-width: 240px;
|
||||
font-size: 0.8em;
|
||||
padding: 8px 12px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
border: solid 0.5px var(--divider);
|
||||
pointer-events: none;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
Reference in New Issue
Block a user