From 79d592b4318d0a9d69a3469db8a0ef8b35c3af90 Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Fri, 8 Jun 2018 04:21:06 +0900 Subject: [PATCH] =?UTF-8?q?MisskeyDeck:=20=E3=82=AB=E3=83=A9=E3=83=A0?= =?UTF-8?q?=E3=82=92=E3=82=B9=E3=82=BF=E3=83=83=E3=82=AF=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/ja.yml | 2 + .../app/common/views/components/menu.vue | 33 +++++- .../views/pages/deck/deck.column-core.vue | 48 ++++++++ .../desktop/views/pages/deck/deck.column.vue | 61 +++++++--- .../pages/deck/deck.notifications-column.vue | 17 +-- .../views/pages/deck/deck.tl-column.vue | 39 +++---- .../app/desktop/views/pages/deck/deck.vue | 58 +++++++--- .../views/pages/deck/deck.widgets-column.vue | 107 ++++++++---------- src/client/app/store.ts | 53 ++++++--- 9 files changed, 275 insertions(+), 143 deletions(-) create mode 100644 src/client/app/desktop/views/pages/deck/deck.column-core.vue diff --git a/locales/ja.yml b/locales/ja.yml index 320da8e777..3161040ec8 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -88,6 +88,8 @@ common: remove: "カラムを削除" add-column: "カラムを追加" rename: "名前を変更" + stack-left: "左に重ねる" + pop-right: "右に出す" common/views/components/connect-failed.vue: title: "サーバーに接続できません" diff --git a/src/client/app/common/views/components/menu.vue b/src/client/app/common/views/components/menu.vue index 73c8403ad3..8874e4c49b 100644 --- a/src/client/app/common/views/components/menu.vue +++ b/src/client/app/common/views/components/menu.vue @@ -1,7 +1,7 @@ <template> <div class="mk-menu"> <div class="backdrop" ref="backdrop" @click="close"></div> - <div class="popover" :class="{ compact }" ref="popover"> + <div class="popover" :class="{ hukidasi }" ref="popover"> <template v-for="item in items"> <div v-if="item == null"></div> <button v-else @click="clicked(item.onClick)" v-html="item.content"></button> @@ -16,6 +16,11 @@ import * as anime from 'animejs'; export default Vue.extend({ props: ['source', 'compact', 'items'], + data() { + return { + hukidasi: !this.compact + }; + }, mounted() { this.$nextTick(() => { const popover = this.$refs.popover as any; @@ -24,18 +29,34 @@ export default Vue.extend({ const width = popover.offsetWidth; const height = popover.offsetHeight; + let left; + let top; + 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'; + left = (x - (width / 2)); + top = (y - (height / 2)); } 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'; + left = (x - (width / 2)); + top = y; } + if (left + width > window.innerWidth) { + left = window.innerWidth - width; + this.hukidasi = false; + } + + if (top + height > window.innerHeight) { + top = window.innerHeight - height; + this.hukidasi = false; + } + + popover.style.left = left + 'px'; + popover.style.top = top + 'px'; + anime({ targets: this.$refs.backdrop, opacity: 1, @@ -113,7 +134,7 @@ $border-color = rgba(27, 31, 35, 0.15) $balloon-size = 16px - &:not(.compact) + &.hukidasi margin-top $balloon-size transform-origin center -($balloon-size) diff --git a/src/client/app/desktop/views/pages/deck/deck.column-core.vue b/src/client/app/desktop/views/pages/deck/deck.column-core.vue new file mode 100644 index 0000000000..836ce3ac9e --- /dev/null +++ b/src/client/app/desktop/views/pages/deck/deck.column-core.vue @@ -0,0 +1,48 @@ +<template> +<x-widgets-column v-if="column.type == 'widgets'"/> +<x-notifications-column v-else-if="column.type == 'notifications'"/> +<x-tl-column v-else-if="column.type == 'home'"/> +<x-tl-column v-else-if="column.type == 'local'"/> +<x-tl-column v-else-if="column.type == 'global'"/> +<x-tl-column v-else-if="column.type == 'list'"/> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import XTlColumn from './deck.tl-column.vue'; +import XNotificationsColumn from './deck.notifications-column.vue'; +import XWidgetsColumn from './deck.widgets-column.vue'; + +export default Vue.extend({ + components: { + XTlColumn, + XNotificationsColumn, + XWidgetsColumn + }, + + props: { + column: { + type: Object, + required: true + }, + isStacked: { + type: Boolean, + required: false, + default: false + }, + isActive: { + type: Boolean, + required: false, + default: true + } + }, + + provide() { + return { + column: this.column, + isStacked: this.isStacked, + isActive: this.isActive + }; + } +}); +</script> diff --git a/src/client/app/desktop/views/pages/deck/deck.column.vue b/src/client/app/desktop/views/pages/deck/deck.column.vue index 4e37c3cbdb..172880df6e 100644 --- a/src/client/app/desktop/views/pages/deck/deck.column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.column.vue @@ -1,10 +1,10 @@ <template> -<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow }"> - <header :class="{ indicate }"> +<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow, isActive, isStacked }"> + <header :class="{ indicate }" @click="toggleActive"> <slot name="header"></slot> - <button ref="menu" @click="showMenu">%fa:caret-down%</button> + <button ref="menu" @click.stop="showMenu">%fa:caret-down%</button> </header> - <div ref="body"> + <div ref="body" v-show="isActive"> <slot></slot> </div> </div> @@ -16,17 +16,14 @@ import Menu from '../../../../common/views/components/menu.vue'; export default Vue.extend({ props: { - id: { - type: String, - required: false - }, name: { type: String, required: false }, menu: { type: Array, - required: false + required: false, + default: null }, naked: { type: Boolean, @@ -40,9 +37,17 @@ export default Vue.extend({ } }, + inject: { + column: { from: 'column' }, + _isActive: { from: 'isActive' }, + isStacked: { from: 'isStacked' }, + getColumnVm: { from: 'getColumnVm' } + }, + data() { return { - indicate: false + indicate: false, + isActive: this._isActive }; }, @@ -62,6 +67,13 @@ export default Vue.extend({ }, methods: { + toggleActive() { + if (!this.isStacked) return; + const vms = this.$store.state.settings.deck.layout.find(ids => ids.indexOf(this.column.id) != -1).map(id => this.getColumnVm(id)); + if (this.isActive && vms.filter(vm => vm.$el.classList.contains('isActive')).length == 1) return; + this.isActive = !this.isActive; + }, + isScrollTop() { return this.$refs.body.scrollTop == 0; }, @@ -86,23 +98,33 @@ export default Vue.extend({ default: this.name, allowEmpty: false }).then(name => { - this.$store.dispatch('settings/renameDeckColumn', { id: this.id, name }); + this.$store.dispatch('settings/renameDeckColumn', { id: this.column.id, name }); }); } }, null, { content: '%fa:arrow-left% %i18n:common.deck.swap-left%', onClick: () => { - this.$store.dispatch('settings/swapLeftDeckColumn', this.id); + this.$store.dispatch('settings/swapLeftDeckColumn', this.column.id); } }, { content: '%fa:arrow-right% %i18n:common.deck.swap-right%', onClick: () => { - this.$store.dispatch('settings/swapRightDeckColumn', this.id); + this.$store.dispatch('settings/swapRightDeckColumn', this.column.id); + } + }, null, { + content: '%fa:window-restore R% %i18n:common.deck.stack-left%', + onClick: () => { + this.$store.dispatch('settings/stackLeftDeckColumn', this.column.id); + } + }, { + content: '%fa:window-restore R% %i18n:common.deck.pop-right%', + onClick: () => { + this.$store.dispatch('settings/popRightDeckColumn', this.column.id); } }, null, { content: '%fa:trash-alt R% %i18n:common.deck.remove%', onClick: () => { - this.$store.dispatch('settings/removeDeckColumn', this.id); + this.$store.dispatch('settings/removeDeckColumn', this.column.id); } }]; @@ -128,14 +150,20 @@ root(isDark) $header-height = 42px width 330px + min-width 330px height 100% background isDark ? #282C37 : #fff border-radius 6px box-shadow 0 2px 16px rgba(#000, 0.1) overflow hidden - &.narrow + &:not(.isActive) + flex-basis $header-height + min-height $header-height + + &:not(.isStacked).narrow width 285px + min-width 285px &.naked background rgba(#000, isDark ? 0.25 : 0.1) @@ -157,6 +185,9 @@ root(isDark) background isDark ? #313543 : #fff box-shadow 0 1px rgba(#000, 0.15) + &, * + user-select none + &.indicate box-shadow 0 3px 0 0 $theme-color diff --git a/src/client/app/desktop/views/pages/deck/deck.notifications-column.vue b/src/client/app/desktop/views/pages/deck/deck.notifications-column.vue index e01f91c24d..87f16211fc 100644 --- a/src/client/app/desktop/views/pages/deck/deck.notifications-column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.notifications-column.vue @@ -1,11 +1,9 @@ <template> -<div> - <x-column :id="column.id" :name="name"> - <span slot="header">%fa:bell R%{{ name }}</span> +<x-column :name="name"> + <span slot="header">%fa:bell R%{{ name }}</span> - <x-notifications/> - </x-column> -</div> + <x-notifications/> +</x-column> </template> <script lang="ts"> @@ -19,12 +17,7 @@ export default Vue.extend({ XNotifications }, - props: { - column: { - type: Object, - required: true - } - }, + inject: ['column'], computed: { name(): string { diff --git a/src/client/app/desktop/views/pages/deck/deck.tl-column.vue b/src/client/app/desktop/views/pages/deck/deck.tl-column.vue index 1a5075396b..46d56bb055 100644 --- a/src/client/app/desktop/views/pages/deck/deck.tl-column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.tl-column.vue @@ -1,22 +1,20 @@ <template> -<div> - <x-column :id="column.id" :menu="menu" :name="name"> - <span slot="header"> - <template v-if="column.type == 'home'">%fa:home%</template> - <template v-if="column.type == 'local'">%fa:R comments%</template> - <template v-if="column.type == 'global'">%fa:globe%</template> - <template v-if="column.type == 'list'">%fa:list%</template> - <span>{{ name }}</span> - </span> +<x-column :menu="menu" :name="name"> + <span slot="header"> + <template v-if="column.type == 'home'">%fa:home%</template> + <template v-if="column.type == 'local'">%fa:R comments%</template> + <template v-if="column.type == 'global'">%fa:globe%</template> + <template v-if="column.type == 'list'">%fa:list%</template> + <span>{{ name }}</span> + </span> - <div class="editor" v-if="edit"> - <mk-switch v-model="column.isMediaOnly" @change="onChangeSettings" text="%i18n:@is-media-only%"/> - <mk-switch v-model="column.isMediaView" @change="onChangeSettings" text="%i18n:@is-media-view%"/> - </div> - <x-list-tl v-if="column.type == 'list'" :list="column.list" :media-only="column.isMediaOnly"/> - <x-tl v-else :src="column.type" :media-only="column.isMediaOnly"/> - </x-column> -</div> + <div class="editor" v-if="edit"> + <mk-switch v-model="column.isMediaOnly" @change="onChangeSettings" text="%i18n:@is-media-only%"/> + <mk-switch v-model="column.isMediaView" @change="onChangeSettings" text="%i18n:@is-media-view%"/> + </div> + <x-list-tl v-if="column.type == 'list'" :list="column.list" :media-only="column.isMediaOnly"/> + <x-tl v-else :src="column.type" :media-only="column.isMediaOnly"/> +</x-column> </template> <script lang="ts"> @@ -32,12 +30,7 @@ export default Vue.extend({ XListTl }, - props: { - column: { - type: Object, - required: true - } - }, + inject: ['column'], data() { return { diff --git a/src/client/app/desktop/views/pages/deck/deck.vue b/src/client/app/desktop/views/pages/deck/deck.vue index 57ec214c3a..fa2d90ed0e 100644 --- a/src/client/app/desktop/views/pages/deck/deck.vue +++ b/src/client/app/desktop/views/pages/deck/deck.vue @@ -1,13 +1,13 @@ <template> <mk-ui :class="$style.root"> <div class="qlvquzbjribqcaozciifydkngcwtyzje" :data-darkmode="$store.state.device.darkmode"> - <template v-for="column in columns"> - <x-widgets-column v-if="column.type == 'widgets'" :key="column.id" :column="column"/> - <x-notifications-column v-if="column.type == 'notifications'" :key="column.id" :column="column"/> - <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 v-for="ids in layout"> + <div v-if="ids.length > 1" class="folder"> + <template v-for="id, i in ids"> + <x-column-core :ref="id" :key="id" :column="columns.find(c => c.id == id)" :is-stacked="true" :is-active="i == 0"/> + </template> + </div> + <x-column-core v-else :ref="ids[0]" :key="ids[0]" :column="columns.find(c => c.id == ids[0])"/> </template> <button ref="add" @click="add" title="%i18n:common.deck.add-column%">%fa:plus%</button> </div> @@ -16,27 +16,34 @@ <script lang="ts"> import Vue from 'vue'; -import XTlColumn from './deck.tl-column.vue'; -import XNotificationsColumn from './deck.notifications-column.vue'; -import XWidgetsColumn from './deck.widgets-column.vue'; +import XColumnCore from './deck.column-core.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({ components: { - XTlColumn, - XNotificationsColumn, - XWidgetsColumn + XColumnCore }, computed: { - columns() { + columns(): any[] { if (this.$store.state.settings.deck == null) return []; return this.$store.state.settings.deck.columns; + }, + layout(): any[] { + if (this.$store.state.settings.deck == null) return []; + if (this.$store.state.settings.deck.layout == null) return this.$store.state.settings.deck.columns.map(c => [c.id]); + return this.$store.state.settings.deck.layout; } }, + provide() { + return { + getColumnVm: this.getColumnVm + }; + }, + created() { if (this.$store.state.settings.deck == null) { const deck = { @@ -58,11 +65,23 @@ export default Vue.extend({ }] }; + deck.layout = deck.columns.map(c => [c.id]); + this.$store.dispatch('settings/set', { key: 'deck', value: deck }); } + + // 互換性のため + if (this.$store.state.settings.deck != null && this.$store.state.settings.deck.layout == null) { + this.$store.dispatch('settings/set', { + key: 'deck', + value: Object.assign({}, this.$store.state.settings.deck, { + layout: this.$store.state.settings.deck.columns.map(c => [c.id]) + }) + }); + } }, mounted() { @@ -74,6 +93,10 @@ export default Vue.extend({ }, methods: { + getColumnVm(id) { + return this.$refs[id][0]; + }, + add() { this.os.new(Menu, { source: this.$refs.add, @@ -159,6 +182,13 @@ root(isDark) &:last-of-type margin-right 0 + &.folder + display flex + flex-direction column + + > *:not(:last-child) + margin-bottom 8px + > * &:first-child margin-left auto diff --git a/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue b/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue index 6bcebd07cc..1f8fd8a9bf 100644 --- a/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.widgets-column.vue @@ -1,57 +1,55 @@ <template> -<div class="wtdtxvecapixsepjtcupubtsmometobz"> - <x-column :id="column.id" :menu="menu" :naked="true" :narrow="true" :name="name"> - <span slot="header">%fa:calculator%{{ name }}</span> +<x-column :menu="menu" :naked="true" :narrow="true" :name="name" class="wtdtxvecapixsepjtcupubtsmometobz"> + <span slot="header">%fa:calculator%{{ name }}</span> - <div class="gqpwvtwtprsbmnssnbicggtwqhmylhnq"> - <template v-if="edit"> - <header> - <select v-model="widgetAdderSelected"> - <option value="profile">%i18n:common.widgets.profile%</option> - <option value="analog-clock">%i18n:common.widgets.analog-clock%</option> - <option value="calendar">%i18n:common.widgets.calendar%</option> - <option value="timemachine">%i18n:common.widgets.timemachine%</option> - <option value="activity">%i18n:common.widgets.activity%</option> - <option value="rss">%i18n:common.widgets.rss%</option> - <option value="trends">%i18n:common.widgets.trends%</option> - <option value="photo-stream">%i18n:common.widgets.photo-stream%</option> - <option value="slideshow">%i18n:common.widgets.slideshow%</option> - <option value="version">%i18n:common.widgets.version%</option> - <option value="broadcast">%i18n:common.widgets.broadcast%</option> - <option value="notifications">%i18n:common.widgets.notifications%</option> - <option value="users">%i18n:common.widgets.users%</option> - <option value="polls">%i18n:common.widgets.polls%</option> - <option value="post-form">%i18n:common.widgets.post-form%</option> - <option value="messaging">%i18n:common.widgets.messaging%</option> - <option value="memo">%i18n:common.widgets.memo%</option> - <option value="server">%i18n:common.widgets.server%</option> - <option value="donation">%i18n:common.widgets.donation%</option> - <option value="nav">%i18n:common.widgets.nav%</option> - <option value="tips">%i18n:common.widgets.tips%</option> - </select> - <button @click="addWidget">%i18n:@add%</button> - </header> - <x-draggable - :list="column.widgets" - :options="{ handle: '.handle', animation: 150 }" - @sort="onWidgetSort" - > - <div v-for="widget in column.widgets" class="customize-container" :key="widget.id"> - <header> - <span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button> - </header> - <div @click="widgetFunc(widget.id)"> - <component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="deck"/> - </div> + <div class="gqpwvtwtprsbmnssnbicggtwqhmylhnq"> + <template v-if="edit"> + <header> + <select v-model="widgetAdderSelected"> + <option value="profile">%i18n:common.widgets.profile%</option> + <option value="analog-clock">%i18n:common.widgets.analog-clock%</option> + <option value="calendar">%i18n:common.widgets.calendar%</option> + <option value="timemachine">%i18n:common.widgets.timemachine%</option> + <option value="activity">%i18n:common.widgets.activity%</option> + <option value="rss">%i18n:common.widgets.rss%</option> + <option value="trends">%i18n:common.widgets.trends%</option> + <option value="photo-stream">%i18n:common.widgets.photo-stream%</option> + <option value="slideshow">%i18n:common.widgets.slideshow%</option> + <option value="version">%i18n:common.widgets.version%</option> + <option value="broadcast">%i18n:common.widgets.broadcast%</option> + <option value="notifications">%i18n:common.widgets.notifications%</option> + <option value="users">%i18n:common.widgets.users%</option> + <option value="polls">%i18n:common.widgets.polls%</option> + <option value="post-form">%i18n:common.widgets.post-form%</option> + <option value="messaging">%i18n:common.widgets.messaging%</option> + <option value="memo">%i18n:common.widgets.memo%</option> + <option value="server">%i18n:common.widgets.server%</option> + <option value="donation">%i18n:common.widgets.donation%</option> + <option value="nav">%i18n:common.widgets.nav%</option> + <option value="tips">%i18n:common.widgets.tips%</option> + </select> + <button @click="addWidget">%i18n:@add%</button> + </header> + <x-draggable + :list="column.widgets" + :options="{ handle: '.handle', animation: 150 }" + @sort="onWidgetSort" + > + <div v-for="widget in column.widgets" class="customize-container" :key="widget.id"> + <header> + <span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button> + </header> + <div @click="widgetFunc(widget.id)"> + <component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="deck"/> </div> - </x-draggable> - </template> - <template v-else> - <component class="widget" v-for="widget in column.widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" platform="deck"/> - </template> - </div> - </x-column> -</div> + </div> + </x-draggable> + </template> + <template v-else> + <component class="widget" v-for="widget in column.widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" platform="deck"/> + </template> + </div> +</x-column> </template> <script lang="ts"> @@ -66,12 +64,7 @@ export default Vue.extend({ XDraggable }, - props: { - column: { - type: Object, - required: true - } - }, + inject: ['column'], data() { return { diff --git a/src/client/app/store.ts b/src/client/app/store.ts index d51a3abad1..e78d941d8c 100644 --- a/src/client/app/store.ts +++ b/src/client/app/store.ts @@ -173,23 +173,22 @@ export default (os: MiOS) => new Vuex.Store({ }, addDeckColumn(state, column) { - if (state.deck.columns == null) state.deck.columns = []; state.deck.columns.push(column); + state.deck.layout.push([column.id]); }, removeDeckColumn(state, id) { - if (state.deck.columns == null) return; state.deck.columns = state.deck.columns.filter(c => c.id != id); + state.deck.layout = state.deck.layout.map(ids => ids.filter(x => x != 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]; + state.deck.layout.some((ids, i) => { + if (ids.indexOf(id) != -1) { + const left = state.deck.layout[i - 1]; if (left) { - state.deck.columns[i - 1] = state.deck.columns[i]; - state.deck.columns[i] = left; + state.deck.layout[i - 1] = state.deck.layout[i]; + state.deck.layout[i] = left; } return true; } @@ -197,28 +196,40 @@ export default (os: MiOS) => new Vuex.Store({ }, 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]; + state.deck.layout.some((ids, i) => { + if (ids.indexOf(id) != -1) { + const right = state.deck.layout[i + 1]; if (right) { - state.deck.columns[i + 1] = state.deck.columns[i]; - state.deck.columns[i] = right; + state.deck.layout[i + 1] = state.deck.layout[i]; + state.deck.layout[i] = right; } return true; } }); }, + stackLeftDeckColumn(state, id) { + const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1); + state.deck.layout = state.deck.layout.map(ids => ids.filter(x => x != id)); + const left = state.deck.layout[i - 1]; + if (left) state.deck.layout[i - 1].push(id); + state.deck.layout = state.deck.layout.filter(ids => ids.length > 0); + }, + + popRightDeckColumn(state, id) { + const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1); + state.deck.layout = state.deck.layout.map(ids => ids.filter(x => x != id)); + state.deck.layout.splice(i + 1, 0, [id]); + state.deck.layout = state.deck.layout.filter(ids => ids.length > 0); + }, + addDeckWidget(state, x) { - if (state.deck.columns == null) return; const column = state.deck.columns.find(c => c.id == x.id); if (column == null) return; column.widgets.unshift(x.widget); }, removeDeckWidget(state, x) { - if (state.deck.columns == null) return; const column = state.deck.columns.find(c => c.id == x.id); if (column == null) return; column.widgets = column.widgets.filter(w => w.id != x.widget.id); @@ -277,6 +288,16 @@ export default (os: MiOS) => new Vuex.Store({ ctx.dispatch('saveDeck'); }, + stackLeftDeckColumn(ctx, id) { + ctx.commit('stackLeftDeckColumn', id); + ctx.dispatch('saveDeck'); + }, + + popRightDeckColumn(ctx, id) { + ctx.commit('popRightDeckColumn', id); + ctx.dispatch('saveDeck'); + }, + addDeckWidget(ctx, x) { ctx.commit('addDeckWidget', x); ctx.dispatch('saveDeck');