wip
This commit is contained in:
parent
0d8c83f27c
commit
69b5de3346
11 changed files with 505 additions and 163 deletions
|
@ -1,6 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
import analogClock from './analog-clock.vue';
|
||||
import menu from './menu.vue';
|
||||
import signin from './signin.vue';
|
||||
import signup from './signup.vue';
|
||||
import forkit from './forkit.vue';
|
||||
|
@ -29,6 +30,7 @@ import Othello from './othello.vue';
|
|||
import welcomeTimeline from './welcome-timeline.vue';
|
||||
|
||||
Vue.component('mk-analog-clock', analogClock);
|
||||
Vue.component('mk-menu', menu);
|
||||
Vue.component('mk-signin', signin);
|
||||
Vue.component('mk-signup', signup);
|
||||
Vue.component('mk-forkit', forkit);
|
||||
|
|
153
src/client/app/common/views/components/menu.vue
Normal file
153
src/client/app/common/views/components/menu.vue
Normal file
|
@ -0,0 +1,153 @@
|
|||
<template>
|
||||
<div class="mk-menu">
|
||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||
<div class="popover" :class="{ compact }" ref="popover">
|
||||
<button v-for="item in items" @click="clicked(item.onClick)" v-html="item.content"></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as anime from 'animejs';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['source', 'compact', 'items'],
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const popover = this.$refs.popover as any;
|
||||
|
||||
const rect = this.source.getBoundingClientRect();
|
||||
const width = popover.offsetWidth;
|
||||
const height = popover.offsetHeight;
|
||||
|
||||
if (this.compact) {
|
||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||
const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2);
|
||||
popover.style.left = (x - (width / 2)) + 'px';
|
||||
popover.style.top = (y - (height / 2)) + 'px';
|
||||
} else {
|
||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
|
||||
popover.style.left = (x - (width / 2)) + 'px';
|
||||
popover.style.top = y + 'px';
|
||||
}
|
||||
|
||||
anime({
|
||||
targets: this.$refs.backdrop,
|
||||
opacity: 1,
|
||||
duration: 100,
|
||||
easing: 'linear'
|
||||
});
|
||||
|
||||
anime({
|
||||
targets: this.$refs.popover,
|
||||
opacity: 1,
|
||||
scale: [0.5, 1],
|
||||
duration: 500
|
||||
});
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
clicked(fn) {
|
||||
fn();
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
(this.$refs.backdrop as any).style.pointerEvents = 'none';
|
||||
anime({
|
||||
targets: this.$refs.backdrop,
|
||||
opacity: 0,
|
||||
duration: 200,
|
||||
easing: 'linear'
|
||||
});
|
||||
|
||||
(this.$refs.popover as any).style.pointerEvents = 'none';
|
||||
anime({
|
||||
targets: this.$refs.popover,
|
||||
opacity: 0,
|
||||
scale: 0.5,
|
||||
duration: 200,
|
||||
easing: 'easeInBack',
|
||||
complete: () => {
|
||||
this.$emit('closed');
|
||||
this.$destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
$border-color = rgba(27, 31, 35, 0.15)
|
||||
|
||||
.mk-menu
|
||||
position initial
|
||||
|
||||
> .backdrop
|
||||
position fixed
|
||||
top 0
|
||||
left 0
|
||||
z-index 10000
|
||||
width 100%
|
||||
height 100%
|
||||
background rgba(#000, 0.1)
|
||||
opacity 0
|
||||
|
||||
> .popover
|
||||
position absolute
|
||||
z-index 10001
|
||||
padding 8px 0
|
||||
background #fff
|
||||
border 1px solid $border-color
|
||||
border-radius 4px
|
||||
box-shadow 0 3px 12px rgba(27, 31, 35, 0.15)
|
||||
transform scale(0.5)
|
||||
opacity 0
|
||||
|
||||
$balloon-size = 16px
|
||||
|
||||
&:not(.compact)
|
||||
margin-top $balloon-size
|
||||
transform-origin center -($balloon-size)
|
||||
|
||||
&:before
|
||||
content ""
|
||||
display block
|
||||
position absolute
|
||||
top -($balloon-size * 2)
|
||||
left s('calc(50% - %s)', $balloon-size)
|
||||
border-top solid $balloon-size transparent
|
||||
border-left solid $balloon-size transparent
|
||||
border-right solid $balloon-size transparent
|
||||
border-bottom solid $balloon-size $border-color
|
||||
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
position absolute
|
||||
top -($balloon-size * 2) + 1.5px
|
||||
left s('calc(50% - %s)', $balloon-size)
|
||||
border-top solid $balloon-size transparent
|
||||
border-left solid $balloon-size transparent
|
||||
border-right solid $balloon-size transparent
|
||||
border-bottom solid $balloon-size #fff
|
||||
|
||||
> button
|
||||
display block
|
||||
padding 8px 16px
|
||||
width 100%
|
||||
|
||||
&:hover
|
||||
color $theme-color-foreground
|
||||
background $theme-color
|
||||
text-decoration none
|
||||
|
||||
&:active
|
||||
color $theme-color-foreground
|
||||
background darken($theme-color, 10%)
|
||||
|
||||
</style>
|
|
@ -1,55 +1,41 @@
|
|||
<template>
|
||||
<div class="mk-note-menu">
|
||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||
<div class="popover" :class="{ compact }" ref="popover">
|
||||
<button @click="favorite">%i18n:@favorite%</button>
|
||||
<button v-if="note.userId == $store.state.i.id" @click="pin">%i18n:@pin%</button>
|
||||
<button v-if="note.userId == $store.state.i.id" @click="del">%i18n:@delete%</button>
|
||||
<a v-if="note.uri" :href="note.uri" target="_blank">%i18n:@remote%</a>
|
||||
</div>
|
||||
<div class="mk-note-menu" style="position:initial">
|
||||
<mk-menu ref="menu" :source="source" :compact="compact" :items="items" @closed="$destroy"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as anime from 'animejs';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['note', 'source', 'compact'],
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const popover = this.$refs.popover as any;
|
||||
|
||||
const rect = this.source.getBoundingClientRect();
|
||||
const width = popover.offsetWidth;
|
||||
const height = popover.offsetHeight;
|
||||
|
||||
if (this.compact) {
|
||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||
const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2);
|
||||
popover.style.left = (x - (width / 2)) + 'px';
|
||||
popover.style.top = (y - (height / 2)) + 'px';
|
||||
} else {
|
||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
|
||||
popover.style.left = (x - (width / 2)) + 'px';
|
||||
popover.style.top = y + 'px';
|
||||
computed: {
|
||||
items() {
|
||||
const items = [];
|
||||
items.push({
|
||||
content: '%i18n:@favorite%',
|
||||
onClick: this.favorite
|
||||
});
|
||||
if (this.note.userId == this.$store.state.i.id) {
|
||||
items.push({
|
||||
content: '%i18n:@pin%',
|
||||
onClick: this.pin
|
||||
});
|
||||
items.push({
|
||||
content: '%i18n:@delete%',
|
||||
onClick: this.del
|
||||
});
|
||||
}
|
||||
if (this.note.uri) {
|
||||
items.push({
|
||||
content: '%i18n:@remote%',
|
||||
onClick: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
|
||||
anime({
|
||||
targets: this.$refs.backdrop,
|
||||
opacity: 1,
|
||||
duration: 100,
|
||||
easing: 'linear'
|
||||
});
|
||||
|
||||
anime({
|
||||
targets: this.$refs.popover,
|
||||
opacity: 1,
|
||||
scale: [0.5, 1],
|
||||
duration: 500
|
||||
});
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pin() {
|
||||
|
@ -78,98 +64,8 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
close() {
|
||||
(this.$refs.backdrop as any).style.pointerEvents = 'none';
|
||||
anime({
|
||||
targets: this.$refs.backdrop,
|
||||
opacity: 0,
|
||||
duration: 200,
|
||||
easing: 'linear'
|
||||
});
|
||||
|
||||
(this.$refs.popover as any).style.pointerEvents = 'none';
|
||||
anime({
|
||||
targets: this.$refs.popover,
|
||||
opacity: 0,
|
||||
scale: 0.5,
|
||||
duration: 200,
|
||||
easing: 'easeInBack',
|
||||
complete: () => this.$destroy()
|
||||
});
|
||||
this.$refs.menu.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
$border-color = rgba(27, 31, 35, 0.15)
|
||||
|
||||
.mk-note-menu
|
||||
position initial
|
||||
|
||||
> .backdrop
|
||||
position fixed
|
||||
top 0
|
||||
left 0
|
||||
z-index 10000
|
||||
width 100%
|
||||
height 100%
|
||||
background rgba(#000, 0.1)
|
||||
opacity 0
|
||||
|
||||
> .popover
|
||||
position absolute
|
||||
z-index 10001
|
||||
padding 8px 0
|
||||
background #fff
|
||||
border 1px solid $border-color
|
||||
border-radius 4px
|
||||
box-shadow 0 3px 12px rgba(27, 31, 35, 0.15)
|
||||
transform scale(0.5)
|
||||
opacity 0
|
||||
|
||||
$balloon-size = 16px
|
||||
|
||||
&:not(.compact)
|
||||
margin-top $balloon-size
|
||||
transform-origin center -($balloon-size)
|
||||
|
||||
&:before
|
||||
content ""
|
||||
display block
|
||||
position absolute
|
||||
top -($balloon-size * 2)
|
||||
left s('calc(50% - %s)', $balloon-size)
|
||||
border-top solid $balloon-size transparent
|
||||
border-left solid $balloon-size transparent
|
||||
border-right solid $balloon-size transparent
|
||||
border-bottom solid $balloon-size $border-color
|
||||
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
position absolute
|
||||
top -($balloon-size * 2) + 1.5px
|
||||
left s('calc(50% - %s)', $balloon-size)
|
||||
border-top solid $balloon-size transparent
|
||||
border-left solid $balloon-size transparent
|
||||
border-right solid $balloon-size transparent
|
||||
border-bottom solid $balloon-size #fff
|
||||
|
||||
> button
|
||||
> a
|
||||
display block
|
||||
padding 8px 16px
|
||||
width 100%
|
||||
|
||||
&:hover
|
||||
color $theme-color-foreground
|
||||
background $theme-color
|
||||
text-decoration none
|
||||
|
||||
&:active
|
||||
color $theme-color-foreground
|
||||
background darken($theme-color, 10%)
|
||||
|
||||
</style>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs">
|
||||
<header :class="{ indicate }">
|
||||
<slot name="header"></slot>
|
||||
<button ref="menu" @click="menu">%fa:caret-down%</button>
|
||||
</header>
|
||||
<div ref="body">
|
||||
<slot></slot>
|
||||
|
@ -11,8 +12,16 @@
|
|||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import Menu from '../../../../common/views/components/menu.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
indicate: false
|
||||
|
@ -48,6 +57,29 @@ export default Vue.extend({
|
|||
const current = this.$refs.body.scrollTop + this.$refs.body.clientHeight;
|
||||
if (current > this.$refs.body.scrollHeight - 1) this.$emit('bottom');
|
||||
}
|
||||
},
|
||||
|
||||
menu() {
|
||||
this.os.new(Menu, {
|
||||
source: this.$refs.menu,
|
||||
compact: false,
|
||||
items: [{
|
||||
content: '%fa:arrow-left% %i18n:@swap-left%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/swapLeftDeckColumn', this.id);
|
||||
}
|
||||
}, {
|
||||
content: '%fa:arrow-right% %i18n:@swap-right%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/swapRightDeckColumn', this.id);
|
||||
}
|
||||
}, {
|
||||
content: '%fa:trash-alt R% %i18n:@remove%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/removeDeckColumn', this.id);
|
||||
}
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -57,6 +89,8 @@ export default Vue.extend({
|
|||
@import '~const.styl'
|
||||
|
||||
root(isDark)
|
||||
$header-height = 42px
|
||||
|
||||
flex 1
|
||||
min-width 330px
|
||||
max-width 330px
|
||||
|
@ -68,7 +102,7 @@ root(isDark)
|
|||
|
||||
> header
|
||||
z-index 1
|
||||
line-height 42px
|
||||
line-height $header-height
|
||||
padding 0 16px
|
||||
color isDark ? #e3e5e8 : #888
|
||||
background isDark ? #313543 : #fff
|
||||
|
@ -77,8 +111,26 @@ root(isDark)
|
|||
&.indicate
|
||||
box-shadow 0 3px 0 0 $theme-color
|
||||
|
||||
> span
|
||||
[data-fa]
|
||||
margin-right 8px
|
||||
|
||||
> button
|
||||
position absolute
|
||||
top 0
|
||||
right 0
|
||||
width $header-height
|
||||
line-height $header-height
|
||||
color isDark ? #9baec8 : #ccc
|
||||
|
||||
&:hover
|
||||
color isDark ? #b2c1d5 : #aaa
|
||||
|
||||
&:active
|
||||
color isDark ? #b2c1d5 : #999
|
||||
|
||||
> div
|
||||
height calc(100% - 42px)
|
||||
height calc(100% - $header-height)
|
||||
overflow auto
|
||||
overflow-x hidden
|
||||
|
||||
|
|
103
src/client/app/desktop/views/pages/deck/deck.list-tl.vue
Normal file
103
src/client/app/desktop/views/pages/deck/deck.list-tl.vue
Normal file
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<x-notes ref="timeline" :more="existMore ? more : null"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
import { UserListStream } from '../../../../common/scripts/streaming/user-list';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
|
||||
props: {
|
||||
list: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
existMore: false,
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.connection) this.connection.close();
|
||||
this.connection = new UserListStream((this as any).os, this.$store.state.i, this.list.id);
|
||||
this.connection.on('note', this.onNote);
|
||||
this.connection.on('userAdded', this.onUserAdded);
|
||||
this.connection.on('userRemoved', this.onUserRemoved);
|
||||
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.close();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetch() {
|
||||
this.fetching = true;
|
||||
|
||||
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
|
||||
(this as any).api('notes/user-list-timeline', {
|
||||
listId: this.list.id,
|
||||
limit: fetchLimit + 1,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
|
||||
}).then(notes => {
|
||||
if (notes.length == fetchLimit + 1) {
|
||||
notes.pop();
|
||||
this.existMore = true;
|
||||
}
|
||||
res(notes);
|
||||
this.fetching = false;
|
||||
this.$emit('loaded');
|
||||
}, rej);
|
||||
}));
|
||||
},
|
||||
more() {
|
||||
this.moreFetching = true;
|
||||
|
||||
const promise = (this as any).api('notes/user-list-timeline', {
|
||||
listId: this.list.id,
|
||||
limit: fetchLimit + 1,
|
||||
untilId: (this.$refs.timeline as any).tail().id,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
|
||||
});
|
||||
|
||||
promise.then(notes => {
|
||||
if (notes.length == fetchLimit + 1) {
|
||||
notes.pop();
|
||||
} else {
|
||||
this.existMore = false;
|
||||
}
|
||||
notes.forEach(n => (this.$refs.timeline as any).append(n));
|
||||
this.moreFetching = false;
|
||||
});
|
||||
|
||||
return promise;
|
||||
},
|
||||
onNote(note) {
|
||||
// Prepend a note
|
||||
(this.$refs.timeline as any).prepend(note);
|
||||
},
|
||||
onUserAdded() {
|
||||
this.fetch();
|
||||
},
|
||||
onUserRemoved() {
|
||||
this.fetch();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<x-column>
|
||||
<x-column :id="id">
|
||||
<span slot="header">%fa:bell R% %i18n:@notifications%</span>
|
||||
|
||||
<x-notifications/>
|
||||
|
@ -17,6 +17,13 @@ export default Vue.extend({
|
|||
components: {
|
||||
XColumn,
|
||||
XNotifications
|
||||
},
|
||||
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<template>
|
||||
<div>
|
||||
<x-column>
|
||||
<x-column :id="column.id">
|
||||
<span slot="header">
|
||||
<template v-if="src == 'home'">%fa:home% %i18n:@home%</template>
|
||||
<template v-if="src == 'local'">%fa:R comments% %i18n:@local%</template>
|
||||
<template v-if="src == 'global'">%fa:globe% %i18n:@global%</template>
|
||||
<template v-if="src == 'list'">%fa:list% {{ list.title }}</template>
|
||||
<template v-if="column.type == 'home'">%fa:home%%i18n:@home%</template>
|
||||
<template v-if="column.type == 'local'">%fa:R comments%%i18n:@local%</template>
|
||||
<template v-if="column.type == 'global'">%fa:globe%%i18n:@global%</template>
|
||||
<template v-if="column.type == 'list'">%fa:list%{{ column.list.title }}</template>
|
||||
</span>
|
||||
<x-tl :src="src"/>
|
||||
|
||||
<x-list-tl v-if="column.type == 'list'" :list="column.list"/>
|
||||
<x-tl v-else :src="column.type"/>
|
||||
</x-column>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -16,18 +18,20 @@
|
|||
import Vue from 'vue';
|
||||
import XColumn from './deck.column.vue';
|
||||
import XTl from './deck.tl.vue';
|
||||
import XListTl from './deck.list-tl.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XColumn,
|
||||
XTl
|
||||
XTl,
|
||||
XListTl
|
||||
},
|
||||
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
required: false
|
||||
column: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -27,9 +27,7 @@ export default Vue.extend({
|
|||
moreFetching: false,
|
||||
existMore: false,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
unreadCount: 0,
|
||||
date: null
|
||||
connectionId: null
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -74,17 +72,12 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
methods: {
|
||||
mount(root) {
|
||||
this.$refs.timeline.mount(root);
|
||||
},
|
||||
|
||||
fetch() {
|
||||
this.fetching = true;
|
||||
|
||||
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
|
||||
(this as any).api(this.endpoint, {
|
||||
limit: fetchLimit + 1,
|
||||
untilDate: this.date ? this.date.getTime() : undefined,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
|
||||
}).then(notes => {
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
<mk-ui :class="$style.root">
|
||||
<div class="qlvquzbjribqcaozciifydkngcwtyzje" :data-darkmode="$store.state.device.darkmode">
|
||||
<template v-for="column in columns">
|
||||
<x-notifications-column v-if="column.type == 'notifications'" :key="column.id"/>
|
||||
<x-tl-column v-if="column.type == 'home'" :key="column.id" src="home"/>
|
||||
<x-tl-column v-if="column.type == 'local'" :key="column.id" src="local"/>
|
||||
<x-tl-column v-if="column.type == 'global'" :key="column.id" src="global"/>
|
||||
<x-notifications-column v-if="column.type == 'notifications'" :key="column.id" :id="column.id"/>
|
||||
<x-tl-column v-if="column.type == 'home'" :key="column.id" :column="column"/>
|
||||
<x-tl-column v-if="column.type == 'local'" :key="column.id" :column="column"/>
|
||||
<x-tl-column v-if="column.type == 'global'" :key="column.id" :column="column"/>
|
||||
<x-tl-column v-if="column.type == 'list'" :key="column.id" :column="column"/>
|
||||
</template>
|
||||
<button>%fa:plus%</button>
|
||||
<button ref="add" @click="add">%fa:plus%</button>
|
||||
</div>
|
||||
</mk-ui>
|
||||
</template>
|
||||
|
@ -16,6 +17,8 @@
|
|||
import Vue from 'vue';
|
||||
import XTlColumn from './deck.tl-column.vue';
|
||||
import XNotificationsColumn from './deck.notifications-column.vue';
|
||||
import Menu from '../../../../common/views/components/menu.vue';
|
||||
import MkUserListsWindow from '../../components/user-lists-window.vue';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
|
@ -55,6 +58,61 @@ export default Vue.extend({
|
|||
value: deck
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
add() {
|
||||
this.os.new(Menu, {
|
||||
source: this.$refs.add,
|
||||
compact: true,
|
||||
items: [{
|
||||
content: '%i18n:@home%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'home'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:@local%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'local'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:@global%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'global'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:@list%',
|
||||
onClick: () => {
|
||||
const w = (this as any).os.new(MkUserListsWindow);
|
||||
w.$once('choosen', list => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'list',
|
||||
list: list
|
||||
});
|
||||
w.close();
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:@notifications%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'notifications'
|
||||
});
|
||||
}
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -73,12 +73,12 @@ export default class MiOS extends EventEmitter {
|
|||
public app: Vue;
|
||||
|
||||
public new(vm, props) {
|
||||
const w = new vm({
|
||||
const x = new vm({
|
||||
parent: this.app,
|
||||
propsData: props
|
||||
}).$mount();
|
||||
document.body.appendChild(w.$el);
|
||||
return w;
|
||||
document.body.appendChild(x.$el);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -152,6 +152,44 @@ export default (os: MiOS) => new Vuex.Store({
|
|||
|
||||
removeMobileHomeWidget(state, widget) {
|
||||
state.mobileHome = state.mobileHome.filter(w => w.id != widget.id);
|
||||
},
|
||||
|
||||
addDeckColumn(state, column) {
|
||||
if (state.deck.columns == null) state.deck.columns = [];
|
||||
state.deck.columns.push(column);
|
||||
},
|
||||
|
||||
removeDeckColumn(state, id) {
|
||||
if (state.deck.columns == null) return;
|
||||
state.deck.columns = state.deck.columns.filter(c => c.id != id);
|
||||
},
|
||||
|
||||
swapLeftDeckColumn(state, id) {
|
||||
if (state.deck.columns == null) return;
|
||||
state.deck.columns.some((c, i) => {
|
||||
if (c.id == id) {
|
||||
const left = state.deck.columns[i - 1];
|
||||
if (left) {
|
||||
state.deck.columns[i - 1] = state.deck.columns[i];
|
||||
state.deck.columns[i] = left;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
swapRightDeckColumn(state, id) {
|
||||
if (state.deck.columns == null) return;
|
||||
state.deck.columns.some((c, i) => {
|
||||
if (c.id == id) {
|
||||
const right = state.deck.columns[i + 1];
|
||||
if (right) {
|
||||
state.deck.columns[i + 1] = state.deck.columns[i];
|
||||
state.deck.columns[i] = right;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -174,6 +212,42 @@ export default (os: MiOS) => new Vuex.Store({
|
|||
}
|
||||
},
|
||||
|
||||
addDeckColumn(ctx, column) {
|
||||
ctx.commit('addDeckColumn', column);
|
||||
|
||||
os.api('i/update_client_setting', {
|
||||
name: 'deck',
|
||||
value: ctx.state.deck
|
||||
});
|
||||
},
|
||||
|
||||
removeDeckColumn(ctx, id) {
|
||||
ctx.commit('removeDeckColumn', id);
|
||||
|
||||
os.api('i/update_client_setting', {
|
||||
name: 'deck',
|
||||
value: ctx.state.deck
|
||||
});
|
||||
},
|
||||
|
||||
swapLeftDeckColumn(ctx, id) {
|
||||
ctx.commit('swapLeftDeckColumn', id);
|
||||
|
||||
os.api('i/update_client_setting', {
|
||||
name: 'deck',
|
||||
value: ctx.state.deck
|
||||
});
|
||||
},
|
||||
|
||||
swapRightDeckColumn(ctx, id) {
|
||||
ctx.commit('swapRightDeckColumn', id);
|
||||
|
||||
os.api('i/update_client_setting', {
|
||||
name: 'deck',
|
||||
value: ctx.state.deck
|
||||
});
|
||||
},
|
||||
|
||||
addHomeWidget(ctx, widget) {
|
||||
ctx.commit('addHomeWidget', widget);
|
||||
|
||||
|
|
Loading…
Reference in a new issue