Classic UI
This commit is contained in:
		
							parent
							
								
									04e27e160e
								
							
						
					
					
						commit
						42d293ee60
					
				
					 19 changed files with 391 additions and 67 deletions
				
			
		|  | @ -528,7 +528,7 @@ removeAllFollowing: "フォローを全解除" | ||||||
| removeAllFollowingDescription: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。" | removeAllFollowingDescription: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。" | ||||||
| userSuspended: "このユーザーは凍結されています。" | userSuspended: "このユーザーは凍結されています。" | ||||||
| userSilenced: "このユーザーはサイレンスされています。" | userSilenced: "このユーザーはサイレンスされています。" | ||||||
| sidebar: "サイドバー" | menu: "メニュー" | ||||||
| divider: "分割線" | divider: "分割線" | ||||||
| addItem: "項目を追加" | addItem: "項目を追加" | ||||||
| rooms: "ルーム" | rooms: "ルーム" | ||||||
|  | @ -927,9 +927,10 @@ _channel: | ||||||
|   usersCount: "{n}人が参加中" |   usersCount: "{n}人が参加中" | ||||||
|   notesCount: "{n}投稿があります" |   notesCount: "{n}投稿があります" | ||||||
| 
 | 
 | ||||||
| _sidebar: | _menuDisplay: | ||||||
|   full: "フル" |   sideFull: "横" | ||||||
|   icon: "アイコン" |   sideIcon: "横(アイコン)" | ||||||
|  |   top: "上部" | ||||||
|   hide: "隠す" |   hide: "隠す" | ||||||
| 
 | 
 | ||||||
| _wordMute: | _wordMute: | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import { defineComponent } from 'vue'; | import { defineComponent } from 'vue'; | ||||||
| import MkModal from '@client/components/ui/modal.vue'; | import MkModal from '@client/components/ui/modal.vue'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import { instanceName } from '@client/config'; | import { instanceName } from '@client/config'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
|  | @ -48,7 +48,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	data() { | 	data() { | ||||||
| 		return { | 		return { | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			items: [], | 			items: [], | ||||||
| 			instanceName, | 			instanceName, | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
|  | @ -191,6 +191,8 @@ export default defineComponent({ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	> .content { | 	> .content { | ||||||
|  | 		--stickyTop: 0px; | ||||||
|  | 
 | ||||||
| 		&.omitted { | 		&.omitted { | ||||||
| 			position: relative; | 			position: relative; | ||||||
| 			max-height: var(--maxHeight); | 			max-height: var(--maxHeight); | ||||||
|  |  | ||||||
|  | @ -43,6 +43,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	props: { | 	props: { | ||||||
| 		widgets: { | 		widgets: { | ||||||
|  | 			type: Array, | ||||||
| 			required: true, | 			required: true, | ||||||
| 		}, | 		}, | ||||||
| 		edit: { | 		edit: { | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import { i18n } from '@client/i18n'; | ||||||
| import { $i } from './account'; | import { $i } from './account'; | ||||||
| import { unisonReload } from '@client/scripts/unison-reload'; | import { unisonReload } from '@client/scripts/unison-reload'; | ||||||
| 
 | 
 | ||||||
| export const sidebarDef = { | export const menuDef = { | ||||||
| 	notifications: { | 	notifications: { | ||||||
| 		title: 'notifications', | 		title: 'notifications', | ||||||
| 		icon: 'fas fa-bell', | 		icon: 'fas fa-bell', | ||||||
|  | @ -26,7 +26,7 @@ | ||||||
| 				<template #label>{{ $ts.clientSettings }}</template> | 				<template #label>{{ $ts.clientSettings }}</template> | ||||||
| 				<FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</FormLink> | 				<FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</FormLink> | ||||||
| 				<FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</FormLink> | 				<FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</FormLink> | ||||||
| 				<FormLink :active="page === 'sidebar'" replace to="/settings/sidebar"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.sidebar }}</FormLink> | 				<FormLink :active="page === 'menu'" replace to="/settings/menu"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.menu }}</FormLink> | ||||||
| 				<FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</FormLink> | 				<FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</FormLink> | ||||||
| 				<FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</FormLink> | 				<FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</FormLink> | ||||||
| 			</FormGroup> | 			</FormGroup> | ||||||
|  | @ -121,7 +121,7 @@ export default defineComponent({ | ||||||
| 				case 'theme': return defineAsyncComponent(() => import('./theme.vue')); | 				case 'theme': return defineAsyncComponent(() => import('./theme.vue')); | ||||||
| 				case 'theme/install': return defineAsyncComponent(() => import('./theme.install.vue')); | 				case 'theme/install': return defineAsyncComponent(() => import('./theme.install.vue')); | ||||||
| 				case 'theme/manage': return defineAsyncComponent(() => import('./theme.manage.vue')); | 				case 'theme/manage': return defineAsyncComponent(() => import('./theme.manage.vue')); | ||||||
| 				case 'sidebar': return defineAsyncComponent(() => import('./sidebar.vue')); | 				case 'menu': return defineAsyncComponent(() => import('./menu.vue')); | ||||||
| 				case 'sounds': return defineAsyncComponent(() => import('./sounds.vue')); | 				case 'sounds': return defineAsyncComponent(() => import('./sounds.vue')); | ||||||
| 				case 'custom-css': return defineAsyncComponent(() => import('./custom-css.vue')); | 				case 'custom-css': return defineAsyncComponent(() => import('./custom-css.vue')); | ||||||
| 				case 'deck': return defineAsyncComponent(() => import('./deck.vue')); | 				case 'deck': return defineAsyncComponent(() => import('./deck.vue')); | ||||||
|  |  | ||||||
|  | @ -1,18 +1,18 @@ | ||||||
| <template> | <template> | ||||||
| <FormBase> | <FormBase> | ||||||
| 	<FormTextarea v-model:value="items" tall> | 	<FormTextarea v-model:value="items" tall manual-save> | ||||||
| 		<span>{{ $ts.sidebar }}</span> | 		<span>{{ $ts.menu }}</span> | ||||||
| 		<template #desc><button class="_textButton" @click="addItem">{{ $ts.addItem }}</button></template> | 		<template #desc><button class="_textButton" @click="addItem">{{ $ts.addItem }}</button></template> | ||||||
| 	</FormTextarea> | 	</FormTextarea> | ||||||
| 
 | 
 | ||||||
| 	<FormRadios v-model="sidebarDisplay"> | 	<FormRadios v-model="menuDisplay"> | ||||||
| 		<template #desc>{{ $ts.display }}</template> | 		<template #desc>{{ $ts.display }}</template> | ||||||
| 		<option value="full">{{ $ts._sidebar.full }}</option> | 		<option value="sideFull">{{ $ts._menuDisplay.sideFull }}</option> | ||||||
| 		<option value="icon">{{ $ts._sidebar.icon }}</option> | 		<option value="sideIcon">{{ $ts._menuDisplay.sideIcon }}</option> | ||||||
| 		<!-- <MkRadio v-model="sidebarDisplay" value="hide" disabled>{{ $ts._sidebar.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 --> | 		<option value="top">{{ $ts._menuDisplay.top }}</option> | ||||||
|  | 		<!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ $ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 --> | ||||||
| 	</FormRadios> | 	</FormRadios> | ||||||
| 
 | 
 | ||||||
| 	<FormButton @click="save()" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> |  | ||||||
| 	<FormButton @click="reset()" danger><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton> | 	<FormButton @click="reset()" danger><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton> | ||||||
| </FormBase> | </FormBase> | ||||||
| </template> | </template> | ||||||
|  | @ -26,7 +26,7 @@ import FormBase from '@client/components/form/base.vue'; | ||||||
| import FormGroup from '@client/components/form/group.vue'; | import FormGroup from '@client/components/form/group.vue'; | ||||||
| import FormButton from '@client/components/form/button.vue'; | import FormButton from '@client/components/form/button.vue'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import { defaultStore } from '@client/store'; | import { defaultStore } from '@client/store'; | ||||||
| import * as symbols from '@client/symbols'; | import * as symbols from '@client/symbols'; | ||||||
| import { unisonReload } from '@client/scripts/unison-reload'; | import { unisonReload } from '@client/scripts/unison-reload'; | ||||||
|  | @ -44,11 +44,11 @@ export default defineComponent({ | ||||||
| 	data() { | 	data() { | ||||||
| 		return { | 		return { | ||||||
| 			[symbols.PAGE_INFO]: { | 			[symbols.PAGE_INFO]: { | ||||||
| 				title: this.$ts.sidebar, | 				title: this.$ts.menu, | ||||||
| 				icon: 'fas fa-list-ul' | 				icon: 'fas fa-list-ul' | ||||||
| 			}, | 			}, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			items: '', | 			items: defaultStore.state.menu.join('\n'), | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -57,11 +57,17 @@ export default defineComponent({ | ||||||
| 			return this.items.trim().split('\n').filter(x => x.trim() !== ''); | 			return this.items.trim().split('\n').filter(x => x.trim() !== ''); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		sidebarDisplay: defaultStore.makeGetterSetter('sidebarDisplay') | 		menuDisplay: defaultStore.makeGetterSetter('menuDisplay') | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	created() { | 	watch: { | ||||||
| 		this.items = this.$store.state.menu.join('\n'); | 		menuDisplay() { | ||||||
|  | 			this.reloadAsk(); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		items() { | ||||||
|  | 			this.save(); | ||||||
|  | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
|  | @ -85,7 +91,6 @@ export default defineComponent({ | ||||||
| 			}); | 			}); | ||||||
| 			if (canceled) return; | 			if (canceled) return; | ||||||
| 			this.items = [...this.splited, item].join('\n'); | 			this.items = [...this.splited, item].join('\n'); | ||||||
| 			this.save(); |  | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		save() { | 		save() { | ||||||
|  | @ -96,7 +101,6 @@ export default defineComponent({ | ||||||
| 		reset() { | 		reset() { | ||||||
| 			this.$store.reset('menu'); | 			this.$store.reset('menu'); | ||||||
| 			this.items = this.$store.state.menu.join('\n'); | 			this.items = this.$store.state.menu.join('\n'); | ||||||
| 			this.reloadAsk(); |  | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		async reloadAsk() { | 		async reloadAsk() { | ||||||
|  | @ -7,8 +7,9 @@ export class StickySidebar { | ||||||
| 	private isTop = false; | 	private isTop = false; | ||||||
| 	private isBottom = false; | 	private isBottom = false; | ||||||
| 	private offsetTop: number; | 	private offsetTop: number; | ||||||
|  | 	private globalHeaderHeight: number = 59; | ||||||
| 
 | 
 | ||||||
| 	constructor(container: StickySidebar['container'], marginTop = 0) { | 	constructor(container: StickySidebar['container'], marginTop = 0, globalHeaderHeight = 0) { | ||||||
| 		this.container = container; | 		this.container = container; | ||||||
| 		this.el = this.container.children[0] as HTMLElement; | 		this.el = this.container.children[0] as HTMLElement; | ||||||
| 		this.el.style.position = 'sticky'; | 		this.el.style.position = 'sticky'; | ||||||
|  | @ -16,30 +17,31 @@ export class StickySidebar { | ||||||
| 		this.container.prepend(this.spacer); | 		this.container.prepend(this.spacer); | ||||||
| 		this.marginTop = marginTop; | 		this.marginTop = marginTop; | ||||||
| 		this.offsetTop = this.container.getBoundingClientRect().top; | 		this.offsetTop = this.container.getBoundingClientRect().top; | ||||||
|  | 		this.globalHeaderHeight = globalHeaderHeight; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public calc(scrollTop: number) { | 	public calc(scrollTop: number) { | ||||||
| 		if (scrollTop > this.lastScrollTop) { // downscroll
 | 		if (scrollTop > this.lastScrollTop) { // downscroll
 | ||||||
| 			const overflow = Math.max(0, (this.el.clientHeight + this.marginTop) - window.innerHeight); | 			const overflow = Math.max(0, this.globalHeaderHeight + (this.el.clientHeight + this.marginTop) - window.innerHeight); | ||||||
| 			this.el.style.bottom = null; | 			this.el.style.bottom = null; | ||||||
| 			this.el.style.top = `${-overflow + this.marginTop}px`; | 			this.el.style.top = `${-overflow + this.marginTop + this.globalHeaderHeight}px`; | ||||||
| 
 | 
 | ||||||
| 			this.isBottom = (scrollTop + window.innerHeight) >= (this.el.offsetTop + this.el.clientHeight); | 			this.isBottom = (scrollTop + window.innerHeight) >= (this.el.offsetTop + this.el.clientHeight); | ||||||
| 
 | 
 | ||||||
| 			if (this.isTop) { | 			if (this.isTop) { | ||||||
| 				this.isTop = false; | 				this.isTop = false; | ||||||
| 				this.spacer.style.marginTop = `${Math.max(0, this.lastScrollTop + this.marginTop - this.offsetTop)}px`; | 				this.spacer.style.marginTop = `${Math.max(0, this.globalHeaderHeight + this.lastScrollTop + this.marginTop - this.offsetTop)}px`; | ||||||
| 			} | 			} | ||||||
| 		} else { // upscroll
 | 		} else { // upscroll
 | ||||||
| 			const overflow = (this.el.clientHeight + this.marginTop) - window.innerHeight; | 			const overflow = this.globalHeaderHeight + (this.el.clientHeight + this.marginTop) - window.innerHeight; | ||||||
| 			this.el.style.top = null; | 			this.el.style.top = null; | ||||||
| 			this.el.style.bottom = `${-overflow}px`; | 			this.el.style.bottom = `${-overflow}px`; | ||||||
| 
 | 
 | ||||||
| 			this.isTop = scrollTop <= this.el.offsetTop; | 			this.isTop = scrollTop + this.marginTop + this.globalHeaderHeight <= this.el.offsetTop; | ||||||
| 
 | 
 | ||||||
| 			if (this.isBottom) { | 			if (this.isBottom) { | ||||||
| 				this.isBottom = false; | 				this.isBottom = false; | ||||||
| 				this.spacer.style.marginTop = `${this.lastScrollTop + this.marginTop - this.offsetTop - overflow}px`; | 				this.spacer.style.marginTop = `${this.globalHeaderHeight + this.lastScrollTop + this.marginTop - this.offsetTop - overflow}px`; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -90,6 +90,7 @@ export const defaultStore = markRaw(new Storage('base', { | ||||||
| 		default: [] as { | 		default: [] as { | ||||||
| 			name: string; | 			name: string; | ||||||
| 			id: string; | 			id: string; | ||||||
|  | 			place: string; | ||||||
| 			data: Record<string, any>; | 			data: Record<string, any>; | ||||||
| 		}[] | 		}[] | ||||||
| 	}, | 	}, | ||||||
|  | @ -185,9 +186,9 @@ export const defaultStore = markRaw(new Storage('base', { | ||||||
| 		where: 'device', | 		where: 'device', | ||||||
| 		default: false | 		default: false | ||||||
| 	}, | 	}, | ||||||
| 	sidebarDisplay: { | 	menuDisplay: { | ||||||
| 		where: 'device', | 		where: 'device', | ||||||
| 		default: 'full' as 'full' | 'icon' | 		default: 'sideFull' as 'sideFull' | 'sideIcon' | 'top' | ||||||
| 	}, | 	}, | ||||||
| 	reportError: { | 	reportError: { | ||||||
| 		where: 'device', | 		where: 'device', | ||||||
|  |  | ||||||
|  | @ -161,7 +161,7 @@ hr { | ||||||
| 	background: none; | 	background: none; | ||||||
| 	border: none; | 	border: none; | ||||||
| 	cursor: pointer; | 	cursor: pointer; | ||||||
| 	color: var(--fg); | 	color: inherit; | ||||||
| 	touch-action: manipulation; | 	touch-action: manipulation; | ||||||
| 	tap-highlight-color: transparent; | 	tap-highlight-color: transparent; | ||||||
| 	-webkit-tap-highlight-color: transparent; | 	-webkit-tap-highlight-color: transparent; | ||||||
|  |  | ||||||
|  | @ -49,7 +49,7 @@ import { defineComponent } from 'vue'; | ||||||
| import { host } from '@client/config'; | import { host } from '@client/config'; | ||||||
| import { search } from '@client/scripts/search'; | import { search } from '@client/scripts/search'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import { getAccounts, addAccount, login } from '@client/account'; | import { getAccounts, addAccount, login } from '@client/account'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
|  | @ -67,7 +67,7 @@ export default defineComponent({ | ||||||
| 			showing: false, | 			showing: false, | ||||||
| 			accounts: [], | 			accounts: [], | ||||||
| 			connection: null, | 			connection: null, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			iconOnly: false, | 			iconOnly: false, | ||||||
| 			hidden: this.defaultHidden, | 			hidden: this.defaultHidden, | ||||||
| 		}; | 		}; | ||||||
|  | @ -92,7 +92,7 @@ export default defineComponent({ | ||||||
| 			this.showing = false; | 			this.showing = false; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		'$store.reactiveState.sidebarDisplay.value'() { | 		'$store.reactiveState.menuDisplay.value'() { | ||||||
| 			this.calcViewState(); | 			this.calcViewState(); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | @ -116,7 +116,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		calcViewState() { | 		calcViewState() { | ||||||
| 			this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.sidebarDisplay === 'icon'); | 			this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.menuDisplay === 'sideIcon'); | ||||||
| 			if (!this.defaultHidden) { | 			if (!this.defaultHidden) { | ||||||
| 				this.hidden = (window.innerWidth <= 650); | 				this.hidden = (window.innerWidth <= 650); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -142,7 +142,7 @@ import XTimeline from './timeline.vue'; | ||||||
| import XHeaderClock from './header-clock.vue'; | import XHeaderClock from './header-clock.vue'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import { router } from '@client/router'; | import { router } from '@client/router'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import { search } from '@client/scripts/search'; | import { search } from '@client/scripts/search'; | ||||||
| import copyToClipboard from '@client/scripts/copy-to-clipboard'; | import copyToClipboard from '@client/scripts/copy-to-clipboard'; | ||||||
| import { store } from './store'; | import { store } from './store'; | ||||||
|  | @ -190,7 +190,7 @@ export default defineComponent({ | ||||||
| 			followedChannels: null, | 			followedChannels: null, | ||||||
| 			featuredChannels: null, | 			featuredChannels: null, | ||||||
| 			currentChannel: null, | 			currentChannel: null, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			sideViewOpening: false, | 			sideViewOpening: false, | ||||||
| 			instanceName, | 			instanceName, | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ import DeckColumnCore from '@client/ui/deck/column-core.vue'; | ||||||
| import XSidebar from '@client/ui/_common_/sidebar.vue'; | import XSidebar from '@client/ui/_common_/sidebar.vue'; | ||||||
| import { getScrollContainer } from '@client/scripts/scroll'; | import { getScrollContainer } from '@client/scripts/scroll'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import XCommon from './_common_/common.vue'; | import XCommon from './_common_/common.vue'; | ||||||
| import { deckStore, addColumn, loadDeck } from './deck/deck-store'; | import { deckStore, addColumn, loadDeck } from './deck/deck-store'; | ||||||
| 
 | 
 | ||||||
|  | @ -60,7 +60,7 @@ export default defineComponent({ | ||||||
| 		return { | 		return { | ||||||
| 			deckStore, | 			deckStore, | ||||||
| 			host: host, | 			host: host, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			wallpaper: localStorage.getItem('wallpaper') != null, | 			wallpaper: localStorage.getItem('wallpaper') != null, | ||||||
| 		}; | 		}; | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
							
								
								
									
										274
									
								
								src/client/ui/default.header.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								src/client/ui/default.header.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,274 @@ | ||||||
|  | <template> | ||||||
|  | <div class="azykntjl"> | ||||||
|  | 	<div class="body"> | ||||||
|  | 		<div class="left"> | ||||||
|  | 			<MkA class="item index" active-class="active" to="/" exact v-click-anime v-tooltip="$ts.timeline"> | ||||||
|  | 				<i class="fas fa-home fa-fw"></i> | ||||||
|  | 			</MkA> | ||||||
|  | 			<template v-for="item in menu"> | ||||||
|  | 				<div v-if="item === '-'" class="divider"></div> | ||||||
|  | 				<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to" v-click-anime v-tooltip="$ts[menuDef[item].title]"> | ||||||
|  | 					<i class="fa-fw" :class="menuDef[item].icon"></i> | ||||||
|  | 					<span v-if="menuDef[item].indicated" class="indicator"><i class="fas fa-circle"></i></span> | ||||||
|  | 				</component> | ||||||
|  | 			</template> | ||||||
|  | 			<div class="divider"></div> | ||||||
|  | 			<MkA v-if="$i.isAdmin || $i.isModerator" class="item" active-class="active" to="/instance" :behavior="settingsWindowed ? 'modalWindow' : null" v-click-anime v-tooltip="$ts.instance"> | ||||||
|  | 				<i class="fas fa-server fa-fw"></i> | ||||||
|  | 			</MkA> | ||||||
|  | 			<button class="item _button" @click="more" v-click-anime> | ||||||
|  | 				<i class="fas fa-ellipsis-h fa-fw"></i> | ||||||
|  | 				<span v-if="otherNavItemIndicated" class="indicator"><i class="fas fa-circle"></i></span> | ||||||
|  | 			</button> | ||||||
|  | 		</div> | ||||||
|  | 		<div class="right"> | ||||||
|  | 			<MkA class="item" active-class="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null" v-click-anime v-tooltip="$ts.settings"> | ||||||
|  | 				<i class="fas fa-cog fa-fw"></i> | ||||||
|  | 			</MkA> | ||||||
|  | 			<button class="item _button account" @click="openAccountMenu" v-click-anime> | ||||||
|  | 				<MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/> | ||||||
|  | 			</button> | ||||||
|  | 			<div class="post" @click="post"> | ||||||
|  | 				<MkButton class="button" primary full> | ||||||
|  | 					<i class="fas fa-pencil-alt fa-fw"></i> | ||||||
|  | 				</MkButton> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts"> | ||||||
|  | import { defineComponent } from 'vue'; | ||||||
|  | import { host } from '@client/config'; | ||||||
|  | import { search } from '@client/scripts/search'; | ||||||
|  | import * as os from '@client/os'; | ||||||
|  | import { menuDef } from '@client/menu'; | ||||||
|  | import { getAccounts, addAccount, login } from '@client/account'; | ||||||
|  | import MkButton from '@client/components/ui/button.vue'; | ||||||
|  | 
 | ||||||
|  | export default defineComponent({ | ||||||
|  | 	components: { | ||||||
|  | 		MkButton, | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	data() { | ||||||
|  | 		return { | ||||||
|  | 			host: host, | ||||||
|  | 			accounts: [], | ||||||
|  | 			connection: null, | ||||||
|  | 			menuDef: menuDef, | ||||||
|  | 			settingsWindowed: false, | ||||||
|  | 		}; | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	computed: { | ||||||
|  | 		menu(): string[] { | ||||||
|  | 			return this.$store.state.menu; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		otherNavItemIndicated(): boolean { | ||||||
|  | 			for (const def in this.menuDef) { | ||||||
|  | 				if (this.menu.includes(def)) continue; | ||||||
|  | 				if (this.menuDef[def].indicated) return true; | ||||||
|  | 			} | ||||||
|  | 			return false; | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	watch: { | ||||||
|  | 		'$store.reactiveState.menuDisplay.value'() { | ||||||
|  | 			this.calcViewState(); | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	created() { | ||||||
|  | 		window.addEventListener('resize', this.calcViewState); | ||||||
|  | 		this.calcViewState(); | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	methods: { | ||||||
|  | 		calcViewState() { | ||||||
|  | 			this.settingsWindowed = (window.innerWidth > 1400); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		post() { | ||||||
|  | 			os.post(); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		search() { | ||||||
|  | 			search(); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		async openAccountMenu(ev) { | ||||||
|  | 			const storedAccounts = getAccounts().filter(x => x.id !== this.$i.id); | ||||||
|  | 			const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) }); | ||||||
|  | 
 | ||||||
|  | 			const accountItemPromises = storedAccounts.map(a => new Promise(res => { | ||||||
|  | 				accountsPromise.then(accounts => { | ||||||
|  | 					const account = accounts.find(x => x.id === a.id); | ||||||
|  | 					if (account == null) return res(null); | ||||||
|  | 					res({ | ||||||
|  | 						type: 'user', | ||||||
|  | 						user: account, | ||||||
|  | 						action: () => { this.switchAccount(account); } | ||||||
|  | 					}); | ||||||
|  | 				}); | ||||||
|  | 			})); | ||||||
|  | 
 | ||||||
|  | 			os.modalMenu([...[{ | ||||||
|  | 				type: 'link', | ||||||
|  | 				text: this.$ts.profile, | ||||||
|  | 				to: `/@${ this.$i.username }`, | ||||||
|  | 				avatar: this.$i, | ||||||
|  | 			}, null, ...accountItemPromises, { | ||||||
|  | 				icon: 'fas fa-plus', | ||||||
|  | 				text: this.$ts.addAccount, | ||||||
|  | 				action: () => { | ||||||
|  | 					os.modalMenu([{ | ||||||
|  | 						text: this.$ts.existingAccount, | ||||||
|  | 						action: () => { this.addAccount(); }, | ||||||
|  | 					}, { | ||||||
|  | 						text: this.$ts.createAccount, | ||||||
|  | 						action: () => { this.createAccount(); }, | ||||||
|  | 					}], ev.currentTarget || ev.target); | ||||||
|  | 				}, | ||||||
|  | 			}]], ev.currentTarget || ev.target, { | ||||||
|  | 				align: 'left' | ||||||
|  | 			}); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		more(ev) { | ||||||
|  | 			os.popup(import('@client/components/launch-pad.vue'), {}, { | ||||||
|  | 			}, 'closed'); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		addAccount() { | ||||||
|  | 			os.popup(import('@client/components/signin-dialog.vue'), {}, { | ||||||
|  | 				done: res => { | ||||||
|  | 					addAccount(res.id, res.i); | ||||||
|  | 					os.success(); | ||||||
|  | 				}, | ||||||
|  | 			}, 'closed'); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		createAccount() { | ||||||
|  | 			os.popup(import('@client/components/signup-dialog.vue'), {}, { | ||||||
|  | 				done: res => { | ||||||
|  | 					addAccount(res.id, res.i); | ||||||
|  | 					this.switchAccountWithToken(res.i); | ||||||
|  | 				}, | ||||||
|  | 			}, 'closed'); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		switchAccount(account: any) { | ||||||
|  | 			const storedAccounts = getAccounts(); | ||||||
|  | 			const token = storedAccounts.find(x => x.id === account.id).token; | ||||||
|  | 			this.switchAccountWithToken(token); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		switchAccountWithToken(token: string) { | ||||||
|  | 			login(token); | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .azykntjl { | ||||||
|  | 	$height: 60px; | ||||||
|  | 	$avatar-size: 32px; | ||||||
|  | 	$avatar-margin: 8px; | ||||||
|  | 
 | ||||||
|  | 	position: sticky; | ||||||
|  | 	top: 0; | ||||||
|  | 	z-index: 1000; | ||||||
|  | 	width: 100%; | ||||||
|  | 	height: $height; | ||||||
|  | 	background-color: var(--bg); | ||||||
|  | 
 | ||||||
|  | 	> .body { | ||||||
|  | 		max-width: 1380px; | ||||||
|  | 		margin: 0 auto; | ||||||
|  | 		display: flex; | ||||||
|  | 
 | ||||||
|  | 		> .right, | ||||||
|  | 		> .left { | ||||||
|  | 
 | ||||||
|  | 			> .item { | ||||||
|  | 				position: relative; | ||||||
|  | 				font-size: 0.9em; | ||||||
|  | 				display: inline-block; | ||||||
|  | 				padding: 0 12px; | ||||||
|  | 				line-height: $height; | ||||||
|  | 
 | ||||||
|  | 				> i, | ||||||
|  | 				> .avatar { | ||||||
|  | 					margin-right: 0; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				> i { | ||||||
|  | 					left: 10px; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				> .avatar { | ||||||
|  | 					width: $avatar-size; | ||||||
|  | 					height: $avatar-size; | ||||||
|  | 					vertical-align: middle; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				> .indicator { | ||||||
|  | 					position: absolute; | ||||||
|  | 					top: 0; | ||||||
|  | 					left: 0; | ||||||
|  | 					color: var(--navIndicator); | ||||||
|  | 					font-size: 8px; | ||||||
|  | 					animation: blink 1s infinite; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				&:hover { | ||||||
|  | 					text-decoration: none; | ||||||
|  | 					color: var(--navHoverFg); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				&.active { | ||||||
|  | 					color: var(--navActive); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			> .divider { | ||||||
|  | 				display: inline-block; | ||||||
|  | 				height: 16px; | ||||||
|  | 				margin: 0 10px; | ||||||
|  | 				border-right: solid 0.5px var(--divider); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			> .post { | ||||||
|  | 				display: inline-block; | ||||||
|  | 			 | ||||||
|  | 				> .button { | ||||||
|  | 					width: 40px; | ||||||
|  | 					height: 40px; | ||||||
|  | 					padding: 0; | ||||||
|  | 					min-width: 0; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			> .account { | ||||||
|  | 				display: inline-flex; | ||||||
|  | 				align-items: center; | ||||||
|  | 				vertical-align: top; | ||||||
|  | 				margin-right: 8px; | ||||||
|  | 
 | ||||||
|  | 				> .acct { | ||||||
|  | 					margin-left: 8px; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		> .right { | ||||||
|  | 			margin-left: auto; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -45,7 +45,7 @@ import { defineComponent } from 'vue'; | ||||||
| import { host } from '@client/config'; | import { host } from '@client/config'; | ||||||
| import { search } from '@client/scripts/search'; | import { search } from '@client/scripts/search'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import { getAccounts, addAccount, login } from '@client/account'; | import { getAccounts, addAccount, login } from '@client/account'; | ||||||
| import MkButton from '@client/components/ui/button.vue'; | import MkButton from '@client/components/ui/button.vue'; | ||||||
| import { StickySidebar } from '@client/scripts/sticky-sidebar'; | import { StickySidebar } from '@client/scripts/sticky-sidebar'; | ||||||
|  | @ -62,7 +62,7 @@ export default defineComponent({ | ||||||
| 			host: host, | 			host: host, | ||||||
| 			accounts: [], | 			accounts: [], | ||||||
| 			connection: null, | 			connection: null, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			iconOnly: false, | 			iconOnly: false, | ||||||
| 			settingsWindowed: false, | 			settingsWindowed: false, | ||||||
| 		}; | 		}; | ||||||
|  | @ -83,7 +83,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	watch: { | 	watch: { | ||||||
| 		'$store.reactiveState.sidebarDisplay.value'() { | 		'$store.reactiveState.menuDisplay.value'() { | ||||||
| 			this.calcViewState(); | 			this.calcViewState(); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | @ -108,7 +108,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		calcViewState() { | 		calcViewState() { | ||||||
| 			this.iconOnly = (window.innerWidth <= 1400) || (this.$store.state.sidebarDisplay === 'icon'); | 			this.iconOnly = (window.innerWidth <= 1400) || (this.$store.state.menuDisplay === 'sideIcon'); | ||||||
| 			this.settingsWindowed = (window.innerWidth > 1400); | 			this.settingsWindowed = (window.innerWidth > 1400); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,9 +1,16 @@ | ||||||
| <template> | <template> | ||||||
| <div class="mk-app" :class="{ wallpaper, isMobile }"> | <div class="mk-app" :class="{ wallpaper, isMobile }"> | ||||||
| 	<div class="columns" :class="{ fullView }"> | 	<XHeaderMenu v-if="showMenuOnTop"/> | ||||||
| 		<div class="sidebar" ref="sidebar" v-if="!isMobile"> | 
 | ||||||
| 			<XSidebar/> | 	<div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }"> | ||||||
| 		</div> | 		<template v-if="!isMobile"> | ||||||
|  | 			<div class="sidebar" v-if="!showMenuOnTop"> | ||||||
|  | 				<XSidebar/> | ||||||
|  | 			</div> | ||||||
|  | 			<div class="widgets left" ref="widgetsLeft" v-else> | ||||||
|  | 				<XWidgets @mounted="attachSticky('widgetsLeft')" :place="'left'"/> | ||||||
|  | 			</div> | ||||||
|  | 		</template> | ||||||
| 
 | 
 | ||||||
| 		<main class="main _panel" @contextmenu.stop="onContextmenu"> | 		<main class="main _panel" @contextmenu.stop="onContextmenu"> | ||||||
| 			<header class="header" @click="onHeaderClick"> | 			<header class="header" @click="onHeaderClick"> | ||||||
|  | @ -20,8 +27,8 @@ | ||||||
| 			</div> | 			</div> | ||||||
| 		</main> | 		</main> | ||||||
| 
 | 
 | ||||||
| 		<div v-if="isDesktop" class="widgets" ref="widgets"> | 		<div v-if="isDesktop" class="widgets right" ref="widgetsRight"> | ||||||
| 			<XWidgets @mounted="attachSticky"/> | 			<XWidgets @mounted="attachSticky('widgetsRight')" :place="null"/> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
|  | @ -60,7 +67,7 @@ import XDrawerSidebar from '@client/ui/_common_/sidebar.vue'; | ||||||
| import XCommon from './_common_/common.vue'; | import XCommon from './_common_/common.vue'; | ||||||
| import XHeader from './_common_/header.vue'; | import XHeader from './_common_/header.vue'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import * as symbols from '@client/symbols'; | import * as symbols from '@client/symbols'; | ||||||
| 
 | 
 | ||||||
| const DESKTOP_THRESHOLD = 1100; | const DESKTOP_THRESHOLD = 1100; | ||||||
|  | @ -72,13 +79,14 @@ export default defineComponent({ | ||||||
| 		XSidebar, | 		XSidebar, | ||||||
| 		XDrawerSidebar, | 		XDrawerSidebar, | ||||||
| 		XHeader, | 		XHeader, | ||||||
|  | 		XHeaderMenu: defineAsyncComponent(() => import('./default.header.vue')), | ||||||
| 		XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')), | 		XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')), | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	data() { | 	data() { | ||||||
| 		return { | 		return { | ||||||
| 			pageInfo: null, | 			pageInfo: null, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			isMobile: window.innerWidth <= MOBILE_THRESHOLD, | 			isMobile: window.innerWidth <= MOBILE_THRESHOLD, | ||||||
| 			isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, | 			isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, | ||||||
| 			widgetsShowing: false, | 			widgetsShowing: false, | ||||||
|  | @ -94,6 +102,10 @@ export default defineComponent({ | ||||||
| 				if (this.menuDef[def].indicated) return true; | 				if (this.menuDef[def].indicated) return true; | ||||||
| 			} | 			} | ||||||
| 			return false; | 			return false; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		showMenuOnTop(): boolean { | ||||||
|  | 			return !this.isMobile && this.$store.state.menuDisplay === 'top'; | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -130,8 +142,8 @@ export default defineComponent({ | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		attachSticky() { | 		attachSticky(ref) { | ||||||
| 			const sticky = new StickySidebar(this.$refs.widgets, 16); | 			const sticky = new StickySidebar(this.$refs[ref], this.$store.state.menuDisplay === 'top' ? 0 : 16, this.$store.state.menuDisplay === 'top' ? 60 : 0); // TODO: ヘッダーの高さを60pxと決め打ちしているのを直す | ||||||
| 			window.addEventListener('scroll', () => { | 			window.addEventListener('scroll', () => { | ||||||
| 				sticky.calc(window.scrollY); | 				sticky.calc(window.scrollY); | ||||||
| 			}, { passive: true }); | 			}, { passive: true }); | ||||||
|  | @ -285,7 +297,7 @@ export default defineComponent({ | ||||||
| 			> .header { | 			> .header { | ||||||
| 				position: sticky; | 				position: sticky; | ||||||
| 				z-index: 1000; | 				z-index: 1000; | ||||||
| 				top: 0; | 				top: var(--globalHeaderHeight, 0px); | ||||||
| 				height: $header-height; | 				height: $header-height; | ||||||
| 				line-height: $header-height; | 				line-height: $header-height; | ||||||
| 				-webkit-backdrop-filter: blur(32px); | 				-webkit-backdrop-filter: blur(32px); | ||||||
|  | @ -296,7 +308,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 			> .content { | 			> .content { | ||||||
| 				background: var(--bg); | 				background: var(--bg); | ||||||
| 				--stickyTop: #{$header-height}; | 				--stickyTop: calc(var(--globalHeaderHeight, 0px) + #{$header-height}); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			@media (max-width: 850px) { | 			@media (max-width: 850px) { | ||||||
|  | @ -317,12 +329,31 @@ export default defineComponent({ | ||||||
| 			@media (max-width: $widgets-hide-threshold) { | 			@media (max-width: $widgets-hide-threshold) { | ||||||
| 				display: none; | 				display: none; | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			&.left { | ||||||
|  | 				margin-right: 16px; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		> .sidebar { | 		> .sidebar { | ||||||
| 			margin-top: 16px; | 			margin-top: 16px; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		&.withGlobalHeader { | ||||||
|  | 			--globalHeaderHeight: 60px; // TODO: 60pxと決め打ちしているのを直す | ||||||
|  | 
 | ||||||
|  | 			> .main { | ||||||
|  | 				margin-top: 2px; | ||||||
|  | 				border-radius: var(--radius); | ||||||
|  | 				box-shadow: 0 0 0 2px var(--divider); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			> .widgets { | ||||||
|  | 				--stickyTop: var(--globalHeaderHeight); | ||||||
|  | 				margin-top: 0px; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		@media (max-width: 850px) { | 		@media (max-width: 850px) { | ||||||
| 			margin: 0; | 			margin: 0; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div class="ddiqwdnk"> | <div class="ddiqwdnk"> | ||||||
| 	<XWidgets class="widgets" :edit="editMode" :widgets="$store.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/> | 	<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"/> | 	<MkAd class="a" prefer="square"/> | ||||||
| 
 | 
 | ||||||
| 	<button v-if="editMode" @click="editMode = false" class="_textButton edit" style="font-size: 0.9em;"><i class="fas fa-check"></i> {{ $ts.editWidgetsExit }}</button> | 	<button v-if="editMode" @click="editMode = false" class="_textButton edit" style="font-size: 0.9em;"><i class="fas fa-check"></i> {{ $ts.editWidgetsExit }}</button> | ||||||
|  | @ -11,13 +11,18 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import { defineComponent, defineAsyncComponent } from 'vue'; | import { defineComponent, defineAsyncComponent } from 'vue'; | ||||||
| import XWidgets from '@client/components/widgets.vue'; | import XWidgets from '@client/components/widgets.vue'; | ||||||
| import * as os from '@client/os'; |  | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
| 		XWidgets | 		XWidgets | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | 	props: { | ||||||
|  | 		place: { | ||||||
|  | 			type: String, | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
| 	emits: ['mounted'], | 	emits: ['mounted'], | ||||||
| 
 | 
 | ||||||
| 	data() { | 	data() { | ||||||
|  | @ -34,7 +39,7 @@ export default defineComponent({ | ||||||
| 		addWidget(widget) { | 		addWidget(widget) { | ||||||
| 			this.$store.set('widgets', [{ | 			this.$store.set('widgets', [{ | ||||||
| 				...widget, | 				...widget, | ||||||
| 				place: null, | 				place: this.place, | ||||||
| 			}, ...this.$store.state.widgets]); | 			}, ...this.$store.state.widgets]); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | @ -50,7 +55,10 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		updateWidgets(widgets) { | 		updateWidgets(widgets) { | ||||||
| 			this.$store.set('widgets', widgets); | 			this.$store.set('widgets', [ | ||||||
|  | 				...this.$store.state.widgets.filter(w => w.place !== this.place), | ||||||
|  | 				...widgets | ||||||
|  | 			]); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ import { search } from '@client/scripts/search'; | ||||||
| import XCommon from './_common_/common.vue'; | import XCommon from './_common_/common.vue'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import XSidebar from '@client/ui/_common_/sidebar.vue'; | import XSidebar from '@client/ui/_common_/sidebar.vue'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import { ColdDeviceStorage } from '@client/store'; | import { ColdDeviceStorage } from '@client/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
|  | @ -33,7 +33,7 @@ export default defineComponent({ | ||||||
| 	data() { | 	data() { | ||||||
| 		return { | 		return { | ||||||
| 			host: host, | 			host: host, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			wallpaper: localStorage.getItem('wallpaper') != null, | 			wallpaper: localStorage.getItem('wallpaper') != null, | ||||||
| 		}; | 		}; | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -61,7 +61,7 @@ import XCommon from './_common_/common.vue'; | ||||||
| import XHeader from './_common_/header.vue'; | import XHeader from './_common_/header.vue'; | ||||||
| import XSide from './default.side.vue'; | import XSide from './default.side.vue'; | ||||||
| import * as os from '@client/os'; | import * as os from '@client/os'; | ||||||
| import { sidebarDef } from '@client/sidebar'; | import { menuDef } from '@client/menu'; | ||||||
| import * as symbols from '@client/symbols'; | import * as symbols from '@client/symbols'; | ||||||
| 
 | 
 | ||||||
| const DESKTOP_THRESHOLD = 1100; | const DESKTOP_THRESHOLD = 1100; | ||||||
|  | @ -87,7 +87,7 @@ export default defineComponent({ | ||||||
| 		return { | 		return { | ||||||
| 			pageInfo: null, | 			pageInfo: null, | ||||||
| 			isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, | 			isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, | ||||||
| 			menuDef: sidebarDef, | 			menuDef: menuDef, | ||||||
| 			navHidden: false, | 			navHidden: false, | ||||||
| 			widgetsShowing: false, | 			widgetsShowing: false, | ||||||
| 			wallpaper: localStorage.getItem('wallpaper') != null, | 			wallpaper: localStorage.getItem('wallpaper') != null, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue