enhance(client): Sync widgets (#8512)
* feature: sync widgets among devices * fix * nanka iroiro * classic.widgets.vueの機能をuniversal.widgets.vueに統合 * 左右のウィジェット編集状態を同期するように * 左右やカラム間でウィジェットを行き来できるように * MkWidgetsをCSS Module化 * set min-height: 100px; * fix deck widget * Update packages/client/src/ui/deck/widgets-column.vue Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * merge * Update classic.vue * Delete classic.widgets.vue Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
		
							parent
							
								
									6c4fa1bc8b
								
							
						
					
					
						commit
						e3f2845cf8
					
				
					 8 changed files with 125 additions and 155 deletions
				
			
		| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
Subproject commit cf3ce27b2eb8417233072e3d6d2fb7c5356c2364
 | 
					Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="vjoppmmu">
 | 
					<div :class="$style.root">
 | 
				
			||||||
	<template v-if="edit">
 | 
						<template v-if="edit">
 | 
				
			||||||
		<header>
 | 
							<header :class="$style['edit-header']">
 | 
				
			||||||
			<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
 | 
								<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
 | 
				
			||||||
				<template #label>{{ i18n.ts.selectWidget }}</template>
 | 
									<template #label>{{ i18n.ts.selectWidget }}</template>
 | 
				
			||||||
				<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option>
 | 
									<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option>
 | 
				
			||||||
| 
						 | 
					@ -14,23 +14,34 @@
 | 
				
			||||||
			item-key="id"
 | 
								item-key="id"
 | 
				
			||||||
			handle=".handle"
 | 
								handle=".handle"
 | 
				
			||||||
			:animation="150"
 | 
								:animation="150"
 | 
				
			||||||
 | 
								:group="{ name: 'SortableMkWidgets' }"
 | 
				
			||||||
			@update:model-value="v => emit('updateWidgets', v)"
 | 
								@update:model-value="v => emit('updateWidgets', v)"
 | 
				
			||||||
 | 
								:class="$style['edit-editing']"
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<template #item="{element}">
 | 
								<template #item="{element}">
 | 
				
			||||||
				<div class="customize-container">
 | 
									<div :class="[$style.widget, $style['customize-container']]">
 | 
				
			||||||
					<button class="config _button" @click.prevent.stop="configWidget(element.id)"><i class="ti ti-settings"></i></button>
 | 
										<button :class="$style['customize-container-config']" class="_button" @click.prevent.stop="configWidget(element.id)"><i class="ti ti-settings"></i></button>
 | 
				
			||||||
					<button class="remove _button" @click.prevent.stop="removeWidget(element)"><i class="ti ti-x"></i></button>
 | 
										<button :class="$style['customize-container-remove']" class="_button" @click.prevent.stop="removeWidget(element)"><i class="ti ti-x"></i></button>
 | 
				
			||||||
					<div class="handle">
 | 
										<div class="handle">
 | 
				
			||||||
						<component :is="`mkw-${element.name}`" :ref="el => widgetRefs[element.id] = el" class="widget" :widget="element" @update-props="updateWidget(element.id, $event)"/>
 | 
											<component :is="`mkw-${element.name}`" :ref="el => widgetRefs[element.id] = el" class="widget" :class="$style['customize-container-handle-widget']" :widget="element" @update-props="updateWidget(element.id, $event)"/>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</template>
 | 
								</template>
 | 
				
			||||||
		</Sortable>
 | 
							</Sortable>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
	<component :is="`mkw-${widget.name}`" v-for="widget in widgets" v-else :key="widget.id" :ref="el => widgetRefs[widget.id] = el" class="widget" :widget="widget" @update-props="updateWidget(widget.id, $event)" @contextmenu.stop="onContextmenu(widget, $event)"/>
 | 
						<component :is="`mkw-${widget.name}`" v-for="widget in widgets" v-else :key="widget.id" :ref="el => widgetRefs[widget.id] = el" :class="$style.widget" :widget="widget" @update-props="updateWidget(widget.id, $event)" @contextmenu.stop="onContextmenu(widget, $event)"/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					export type Widget = {
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
						id: string;
 | 
				
			||||||
 | 
						data: Record<string, any>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export type DefaultStoredWidget = {
 | 
				
			||||||
 | 
						place: string | null;
 | 
				
			||||||
 | 
					} & Widget;
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { defineAsyncComponent, reactive, ref, computed } from 'vue';
 | 
					import { defineAsyncComponent, reactive, ref, computed } from 'vue';
 | 
				
			||||||
import { v4 as uuid } from 'uuid';
 | 
					import { v4 as uuid } from 'uuid';
 | 
				
			||||||
| 
						 | 
					@ -43,12 +54,6 @@ import { deepClone } from '@/scripts/clone';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 | 
					const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Widget = {
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
	id: string;
 | 
					 | 
				
			||||||
	data: Record<string, any>;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
	widgets: Widget[];
 | 
						widgets: Widget[];
 | 
				
			||||||
	edit: boolean;
 | 
						edit: boolean;
 | 
				
			||||||
| 
						 | 
					@ -109,11 +114,22 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" module>
 | 
				
			||||||
.vjoppmmu {
 | 
					.root {
 | 
				
			||||||
	container-type: inline-size;
 | 
						container-type: inline-size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> header {
 | 
					.widget {
 | 
				
			||||||
 | 
						contain: content;
 | 
				
			||||||
 | 
						margin: var(--margin) 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&:first-of-type {
 | 
				
			||||||
 | 
							margin-top: 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.edit {
 | 
				
			||||||
 | 
						&-header {
 | 
				
			||||||
		margin: 16px 0;
 | 
							margin: 16px 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		> * {
 | 
							> * {
 | 
				
			||||||
| 
						 | 
					@ -122,44 +138,42 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .widget, .customize-container {
 | 
						&-editing {
 | 
				
			||||||
		contain: content;
 | 
							min-height: 100px;
 | 
				
			||||||
		margin: var(--margin) 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&:first-of-type {
 | 
					 | 
				
			||||||
			margin-top: 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.customize-container {
 | 
					 | 
				
			||||||
		position: relative;
 | 
					 | 
				
			||||||
		cursor: move;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> .config,
 | 
					 | 
				
			||||||
		> .remove {
 | 
					 | 
				
			||||||
			position: absolute;
 | 
					 | 
				
			||||||
			z-index: 10000;
 | 
					 | 
				
			||||||
			top: 8px;
 | 
					 | 
				
			||||||
			width: 32px;
 | 
					 | 
				
			||||||
			height: 32px;
 | 
					 | 
				
			||||||
			color: #fff;
 | 
					 | 
				
			||||||
			background: rgba(#000, 0.7);
 | 
					 | 
				
			||||||
			border-radius: 4px;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> .config {
 | 
					 | 
				
			||||||
			right: 8px + 8px + 32px;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> .remove {
 | 
					 | 
				
			||||||
			right: 8px;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> .handle {
 | 
					 | 
				
			||||||
			> .widget {
 | 
					 | 
				
			||||||
				pointer-events: none;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.customize-container {
 | 
				
			||||||
 | 
						position: relative;
 | 
				
			||||||
 | 
						cursor: move;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&-config,
 | 
				
			||||||
 | 
						&-remove {
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							z-index: 10000;
 | 
				
			||||||
 | 
							top: 8px;
 | 
				
			||||||
 | 
							width: 32px;
 | 
				
			||||||
 | 
							height: 32px;
 | 
				
			||||||
 | 
							color: #fff;
 | 
				
			||||||
 | 
							background: rgba(#000, 0.7);
 | 
				
			||||||
 | 
							border-radius: 4px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&-config {
 | 
				
			||||||
 | 
							right: 8px + 8px + 32px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&-remove {
 | 
				
			||||||
 | 
							right: 8px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&-handle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&-widget {
 | 
				
			||||||
 | 
								pointer-events: none;
 | 
				
			||||||
 | 
							} 
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -122,7 +122,7 @@ export const defaultStore = markRaw(new Storage('base', {
 | 
				
			||||||
		}[],
 | 
							}[],
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	widgets: {
 | 
						widgets: {
 | 
				
			||||||
		where: 'deviceAccount',
 | 
							where: 'account',
 | 
				
			||||||
		default: [] as {
 | 
							default: [] as {
 | 
				
			||||||
			name: string;
 | 
								name: string;
 | 
				
			||||||
			id: string;
 | 
								id: string;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@
 | 
				
			||||||
			<XSidebar/>
 | 
								<XSidebar/>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div v-else ref="widgetsLeft" class="widgets left">
 | 
							<div v-else ref="widgetsLeft" class="widgets left">
 | 
				
			||||||
			<XWidgets :place="'left'" @mounted="attachSticky(widgetsLeft)"/>
 | 
								<XWidgets place="left" @mounted="attachSticky(widgetsLeft)"/>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<main class="main" :style="{ background: pageMetadata?.value?.bg }" @contextmenu.stop="onContextmenu">
 | 
							<main class="main" :style="{ background: pageMetadata?.value?.bg }" @contextmenu.stop="onContextmenu">
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@
 | 
				
			||||||
		</main>
 | 
							</main>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div v-if="isDesktop" ref="widgetsRight" class="widgets right">
 | 
							<div v-if="isDesktop" ref="widgetsRight" class="widgets right">
 | 
				
			||||||
			<XWidgets :place="null" @mounted="attachSticky(widgetsRight)"/>
 | 
								<XWidgets :place="showMenuOnTop ? 'right' : null" @mounted="attachSticky(widgetsRight)"/>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,7 +52,7 @@ import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/script
 | 
				
			||||||
import { defaultStore } from '@/store';
 | 
					import { defaultStore } from '@/store';
 | 
				
			||||||
import { i18n } from '@/i18n';
 | 
					import { i18n } from '@/i18n';
 | 
				
			||||||
const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
 | 
					const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
 | 
				
			||||||
const XWidgets = defineAsyncComponent(() => import('./classic.widgets.vue'));
 | 
					const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DESKTOP_THRESHOLD = 1100;
 | 
					const DESKTOP_THRESHOLD = 1100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,84 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<div class="ddiqwdnk">
 | 
					 | 
				
			||||||
	<XWidgets class="widgets" :edit="editMode" :widgets="$store.reactiveState.widgets.value.filter(w => w.place === place)" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
 | 
					 | 
				
			||||||
	<MkAd class="a" :prefer="['square']"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<button v-if="editMode" class="_textButton edit" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ $ts.editWidgetsExit }}</button>
 | 
					 | 
				
			||||||
	<button v-else class="_textButton edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ $ts.editWidgets }}</button>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import { defineComponent, defineAsyncComponent } from 'vue';
 | 
					 | 
				
			||||||
import XWidgets from '@/components/MkWidgets.vue';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default defineComponent({
 | 
					 | 
				
			||||||
	components: {
 | 
					 | 
				
			||||||
		XWidgets,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	props: {
 | 
					 | 
				
			||||||
		place: {
 | 
					 | 
				
			||||||
			type: String,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	emits: ['mounted'],
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			editMode: false,
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mounted() {
 | 
					 | 
				
			||||||
		this.$emit('mounted', this.$el);
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		addWidget(widget) {
 | 
					 | 
				
			||||||
			this.$store.set('widgets', [{
 | 
					 | 
				
			||||||
				...widget,
 | 
					 | 
				
			||||||
				place: this.place,
 | 
					 | 
				
			||||||
			}, ...this.$store.state.widgets]);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		removeWidget(widget) {
 | 
					 | 
				
			||||||
			this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id !== widget.id));
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		updateWidget({ id, data }) {
 | 
					 | 
				
			||||||
			this.$store.set('widgets', this.$store.state.widgets.map(w => w.id === id ? {
 | 
					 | 
				
			||||||
				...w,
 | 
					 | 
				
			||||||
				data,
 | 
					 | 
				
			||||||
			} : w));
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		updateWidgets(widgets) {
 | 
					 | 
				
			||||||
			this.$store.set('widgets', [
 | 
					 | 
				
			||||||
				...this.$store.state.widgets.filter(w => w.place !== this.place),
 | 
					 | 
				
			||||||
				...widgets,
 | 
					 | 
				
			||||||
			]);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="scss" scoped>
 | 
					 | 
				
			||||||
.ddiqwdnk {
 | 
					 | 
				
			||||||
	position: sticky;
 | 
					 | 
				
			||||||
	height: min-content;
 | 
					 | 
				
			||||||
	box-sizing: border-box;
 | 
					 | 
				
			||||||
	padding-bottom: 8px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> .widgets,
 | 
					 | 
				
			||||||
	> .a {
 | 
					 | 
				
			||||||
		width: 300px;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> .edit {
 | 
					 | 
				
			||||||
		display: block;
 | 
					 | 
				
			||||||
		margin: 16px auto;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="wtdtxvec">
 | 
						<div class="wtdtxvec">
 | 
				
			||||||
		<div v-if="!(column.widgets && column.widgets.length > 0) && !edit" class="intro">{{ i18n.ts._deck.widgetsIntroduction }}</div>
 | 
							<div v-if="!(column.widgets && column.widgets.length > 0) && !edit" class="intro">{{ i18n.ts._deck.widgetsIntroduction }}</div>
 | 
				
			||||||
		<XWidgets :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
 | 
							<XWidgets :edit="edit" :widgets="column.widgets ?? []" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</XColumn>
 | 
					</XColumn>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -273,7 +273,7 @@ const wallpaper = localStorage.getItem('wallpaper') != null;
 | 
				
			||||||
		right: 0;
 | 
							right: 0;
 | 
				
			||||||
		z-index: 1001;
 | 
							z-index: 1001;
 | 
				
			||||||
		height: 100dvh;
 | 
							height: 100dvh;
 | 
				
			||||||
		padding: var(--margin);
 | 
							padding: var(--margin) !important;
 | 
				
			||||||
		box-sizing: border-box;
 | 
							box-sizing: border-box;
 | 
				
			||||||
		overflow: auto;
 | 
							overflow: auto;
 | 
				
			||||||
		overscroll-behavior: contain;
 | 
							overscroll-behavior: contain;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,25 +1,44 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="efzpzdvf">
 | 
					<div class="efzpzdvf" :class="{ universal: !classic, classic }">
 | 
				
			||||||
	<XWidgets :edit="editMode" :widgets="defaultStore.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
 | 
						<XWidgets :edit="editMode" :widgets="widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
 | 
						<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
 | 
				
			||||||
	<button v-else class="_textButton mk-widget-edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
 | 
						<button v-else class="_textButton mk-widget-edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					let editMode = $ref(false);
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { onMounted } from 'vue';
 | 
					import { onMounted } from 'vue';
 | 
				
			||||||
import XWidgets from '@/components/MkWidgets.vue';
 | 
					import XWidgets from '@/components/MkWidgets.vue';
 | 
				
			||||||
import { i18n } from '@/i18n';
 | 
					import { i18n } from '@/i18n';
 | 
				
			||||||
import { defaultStore } from '@/store';
 | 
					import { defaultStore } from '@/store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = withDefaults(defineProps<{
 | 
				
			||||||
 | 
						// null = 全てのウィジェットを表示
 | 
				
			||||||
 | 
						// left = place: leftだけを表示
 | 
				
			||||||
 | 
						// right = rightとnullを表示
 | 
				
			||||||
 | 
						place?: 'left' | null | 'right';
 | 
				
			||||||
 | 
						classic?: boolean;
 | 
				
			||||||
 | 
					}>(), {
 | 
				
			||||||
 | 
						place: null,
 | 
				
			||||||
 | 
						classic: false,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
	(ev: 'mounted', el: Element): void;
 | 
						(ev: 'mounted', el?: Element): void;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let editMode = $ref(false);
 | 
					 | 
				
			||||||
let rootEl = $ref<HTMLDivElement>();
 | 
					let rootEl = $ref<HTMLDivElement>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const widgets = $computed(() => {
 | 
				
			||||||
 | 
						if (props.place === null) return defaultStore.reactiveState.widgets.value;
 | 
				
			||||||
 | 
						if (props.place === 'left') return defaultStore.reactiveState.widgets.value.filter(w => w.place === 'left');
 | 
				
			||||||
 | 
						return defaultStore.reactiveState.widgets.value.filter(w => w.place !== 'left');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
	emit('mounted', rootEl);
 | 
						emit('mounted', rootEl);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -27,7 +46,7 @@ onMounted(() => {
 | 
				
			||||||
function addWidget(widget) {
 | 
					function addWidget(widget) {
 | 
				
			||||||
	defaultStore.set('widgets', [{
 | 
						defaultStore.set('widgets', [{
 | 
				
			||||||
		...widget,
 | 
							...widget,
 | 
				
			||||||
		place: null,
 | 
							place: props.place,
 | 
				
			||||||
	}, ...defaultStore.state.widgets]);
 | 
						}, ...defaultStore.state.widgets]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,11 +58,26 @@ function updateWidget({ id, data }) {
 | 
				
			||||||
	defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? {
 | 
						defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? {
 | 
				
			||||||
		...w,
 | 
							...w,
 | 
				
			||||||
		data,
 | 
							data,
 | 
				
			||||||
 | 
							place: props.place,
 | 
				
			||||||
	} : w));
 | 
						} : w));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateWidgets(widgets) {
 | 
					function updateWidgets(thisWidgets) {
 | 
				
			||||||
	defaultStore.set('widgets', widgets);
 | 
						if (props.place === null) {
 | 
				
			||||||
 | 
							defaultStore.set('widgets', thisWidgets);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (props.place === 'left') {
 | 
				
			||||||
 | 
							defaultStore.set('widgets', [
 | 
				
			||||||
 | 
								...thisWidgets.map(w => ({ ...w, place: 'left' })),
 | 
				
			||||||
 | 
								...defaultStore.state.widgets.filter(w => w.place !== 'left' && !thisWidgets.some(t => w.id === t.id)),
 | 
				
			||||||
 | 
							]);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultStore.set('widgets', [
 | 
				
			||||||
 | 
							...defaultStore.state.widgets.filter(w => w.place === 'left' && !thisWidgets.some(t => w.id === t.id)),
 | 
				
			||||||
 | 
							...thisWidgets.map(w => ({ ...w, place: 'right' })),
 | 
				
			||||||
 | 
						]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,11 +86,17 @@ function updateWidgets(widgets) {
 | 
				
			||||||
	position: sticky;
 | 
						position: sticky;
 | 
				
			||||||
	height: min-content;
 | 
						height: min-content;
 | 
				
			||||||
	min-height: 100vh;
 | 
						min-height: 100vh;
 | 
				
			||||||
	padding: var(--margin) 0;
 | 
					 | 
				
			||||||
	box-sizing: border-box;
 | 
						box-sizing: border-box;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.universal {
 | 
				
			||||||
 | 
							padding: var(--margin) 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> * {
 | 
				
			||||||
 | 
								margin: var(--margin) 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> * {
 | 
						> * {
 | 
				
			||||||
		margin: var(--margin) 0;
 | 
					 | 
				
			||||||
		width: 300px;
 | 
							width: 300px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		&:first-child {
 | 
							&:first-child {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue