Create sidebar layout component
This commit is contained in:
parent
4876b7b439
commit
b0a5fac22a
17 changed files with 171 additions and 159 deletions
|
@ -20,7 +20,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<router-view :key="route.fullPath" :class="{ wide: isPublicPage() }" />
|
||||
<router-view :key="route.fullPath" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
22
src/components/SidebarLayout.vue
Normal file
22
src/components/SidebarLayout.vue
Normal file
|
@ -0,0 +1,22 @@
|
|||
<template>
|
||||
<div id="main" :class="{ wide: currentUser === null }">
|
||||
<div class="content">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { $ } from "vue/macros"
|
||||
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
import Sidebar from "./Sidebar.vue"
|
||||
|
||||
const { currentUser } = $(useCurrentUser())
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../styles/layout";
|
||||
@import "../styles/theme";
|
||||
</style>
|
|
@ -1,17 +1,16 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<div class="content about" v-if="instance">
|
||||
<sidebar-layout>
|
||||
<template #content v-if="instance">
|
||||
<h1>{{ instance.title }}</h1>
|
||||
<div class="description static-text" v-html="renderMarkdown(instance.description)"></div>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { $ } from "vue/macros"
|
||||
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useInstanceInfo } from "@/store/instance"
|
||||
import { renderMarkdown } from "@/utils/markdown"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<static-page v-if="instance">
|
||||
<static-page v-if="instance" class="wide">
|
||||
<template #heading>{{ instance.title }}</template>
|
||||
<template #text>
|
||||
<div v-html="renderMarkdown(instance.description)"></div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<static-page>
|
||||
<static-page class="wide">
|
||||
<template #heading>Ethereum</template>
|
||||
<template #text>
|
||||
<div v-html="text"></div>
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<div class="content posts">
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<post-editor @post-created="insertPost"></post-editor>
|
||||
<post-list :posts="posts" @load-next-page="loadNextPage"></post-list>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -15,7 +14,7 @@ import { $ref } from "vue/macros"
|
|||
import { Post, getHomeTimeline } from "@/api/posts"
|
||||
import PostEditor from "@/components/PostEditor.vue"
|
||||
import PostList from "@/components/PostList.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
||||
const { ensureAuthToken } = useCurrentUser()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="landing-page">
|
||||
<div class="landing-page wide">
|
||||
<div class="instance-group">
|
||||
<div v-if="instance" class="instance-info">
|
||||
<h1 class="instance-title">{{ instance.title }}</h1>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<div class="content">
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<div
|
||||
class="notification"
|
||||
v-for="(notification, index) in notifications"
|
||||
|
@ -84,9 +84,8 @@
|
|||
>
|
||||
Show more notifications
|
||||
</button>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -97,7 +96,7 @@ import { updateNotificationMarker } from "@/api/markers"
|
|||
import { getNotifications, Notification } from "@/api/notifications"
|
||||
import Avatar from "@/components/Avatar.vue"
|
||||
import Post from "@/components/Post.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useInstanceInfo } from "@/store/instance"
|
||||
import { useNotifications } from "@/store/notifications"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div id="main" ref="containerRef">
|
||||
<div v-if="!isLoading && thread.length === 0" class="content not-found">
|
||||
Not found
|
||||
</div>
|
||||
<div v-else class="content posts">
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<div v-if="!isLoading && thread.length === 0" class="not-found">
|
||||
Not found
|
||||
</div>
|
||||
<post
|
||||
v-for="(post, index) in thread"
|
||||
:key="post.id"
|
||||
|
@ -15,9 +15,8 @@
|
|||
@comment-created="onCommentCreated(index, $event)"
|
||||
@post-deleted="onPostDeleted(index)"
|
||||
></post>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -27,7 +26,7 @@ import { useRoute } from "vue-router"
|
|||
|
||||
import { Post as PostObject, getPostContext } from "@/api/posts"
|
||||
import Post from "@/components/Post.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
||||
const route = useRoute()
|
||||
|
@ -37,7 +36,6 @@ let selectedId = $ref(route.params.postId as string)
|
|||
let highlightedId = $ref<string | null>(null)
|
||||
let thread = $ref<PostObject[]>([])
|
||||
let isLoading = $ref(true)
|
||||
const containerRef = $ref<HTMLElement | null>(null)
|
||||
const loader = $ref(getPostContext(authToken, selectedId))
|
||||
|
||||
onMounted(async () => {
|
||||
|
@ -58,11 +56,12 @@ onMounted(async () => {
|
|||
})
|
||||
|
||||
function scrollTo(postId: string, options: any = {}) {
|
||||
if (containerRef === null) {
|
||||
const container = document.getElementById("main")
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
const containerOffset = containerRef.offsetTop // sticky header height or top margin
|
||||
const postElem: HTMLElement | null = containerRef.querySelector(`div[data-post-id="${postId}"]`)
|
||||
const containerOffset = container.offsetTop // sticky header height or top margin
|
||||
const postElem: HTMLElement | null = container.querySelector(`div[data-post-id="${postId}"]`)
|
||||
if (postElem === null) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-if="post && token" class="post-overlay">
|
||||
<div v-if="post && token" class="post-overlay wide">
|
||||
<div v-if="canGoBack()" class="back-btn-wrapper">
|
||||
<a class="back-btn" title="Back" @click="goBack()">
|
||||
<img :src="require('@/assets/feather/arrow-left.svg')">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="main" v-if="profile">
|
||||
<div class="content posts">
|
||||
<sidebar-layout v-if="profile">
|
||||
<template #content>
|
||||
<div class="profile-block">
|
||||
<div class="profile-header">
|
||||
<img v-if="profile.header" :src="profile.header">
|
||||
|
@ -180,9 +180,8 @@
|
|||
:key="profile.id"
|
||||
></profile-list-item>
|
||||
</template>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -210,7 +209,7 @@ import {
|
|||
import Avatar from "@/components/Avatar.vue"
|
||||
import PostList from "@/components/PostList.vue"
|
||||
import ProfileListItem from "@/components/ProfileListItem.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { BACKEND_URL } from "@/constants"
|
||||
import { useInstanceInfo } from "@/store/instance"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<div class="content profile-list">
|
||||
<router-link
|
||||
v-for="profile in profiles"
|
||||
class="profile-list-item"
|
||||
:to="{ name: 'profile', params: { profileId: profile.id }}"
|
||||
:key="profile.id"
|
||||
>
|
||||
<profile-card :profile="profile" :compact="false"></profile-card>
|
||||
</router-link>
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<div class="profile-grid">
|
||||
<router-link
|
||||
v-for="profile in profiles"
|
||||
class="profile-list-item"
|
||||
:to="{ name: 'profile', params: { profileId: profile.id }}"
|
||||
:key="profile.id"
|
||||
>
|
||||
<profile-card :profile="profile" :compact="false"></profile-card>
|
||||
</router-link>
|
||||
</div>
|
||||
<button
|
||||
v-if="isPageFull()"
|
||||
class="btn secondary next-btn"
|
||||
|
@ -16,9 +18,8 @@
|
|||
>
|
||||
Show more profiles
|
||||
</button>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -28,7 +29,7 @@ import { $ref } from "vue/macros"
|
|||
import { PAGE_SIZE } from "@/api/common"
|
||||
import { Profile, getProfiles } from "@/api/users"
|
||||
import ProfileCard from "@/components/ProfileCard.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
||||
const { ensureAuthToken } = useCurrentUser()
|
||||
|
@ -61,7 +62,7 @@ async function loadNextPage() {
|
|||
@import "../styles/layout";
|
||||
@import "../styles/theme";
|
||||
|
||||
.profile-list {
|
||||
.profile-grid {
|
||||
display: grid;
|
||||
gap: $block-outer-padding;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
|
|
|
@ -1,84 +1,85 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<form class="content settings" @submit.prevent="save()">
|
||||
<h1>Edit profile</h1>
|
||||
<div class="input-group">
|
||||
<label for="display-name">Display name</label>
|
||||
<input id="display-name" v-model.trim="form.display_name">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="bio">Bio</label>
|
||||
<textarea
|
||||
id="bio"
|
||||
ref="bioInputRef"
|
||||
:value="form.note_source || ''"
|
||||
@input="onBioUpdate($event)"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="image-upload-group">
|
||||
<profile-card :profile="profilePreview" :compact="true"></profile-card>
|
||||
<div class="image-upload-inputs">
|
||||
<div class="input-group">
|
||||
<label for="avatar">Avatar</label>
|
||||
<input
|
||||
type="file"
|
||||
id="avatar"
|
||||
accept="image/*"
|
||||
@change="onFilePicked('avatar', $event)"
|
||||
>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="banner">Banner</label>
|
||||
<input
|
||||
type="file"
|
||||
id="banner"
|
||||
accept="image/*"
|
||||
@change="onFilePicked('header', $event)"
|
||||
>
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<form @submit.prevent="save()">
|
||||
<h1>Edit profile</h1>
|
||||
<div class="input-group">
|
||||
<label for="display-name">Display name</label>
|
||||
<input id="display-name" v-model.trim="form.display_name">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="bio">Bio</label>
|
||||
<textarea
|
||||
id="bio"
|
||||
ref="bioInputRef"
|
||||
:value="form.note_source || ''"
|
||||
@input="onBioUpdate($event)"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="image-upload-group">
|
||||
<profile-card :profile="profilePreview" :compact="true"></profile-card>
|
||||
<div class="image-upload-inputs">
|
||||
<div class="input-group">
|
||||
<label for="avatar">Avatar</label>
|
||||
<input
|
||||
type="file"
|
||||
id="avatar"
|
||||
accept="image/*"
|
||||
@change="onFilePicked('avatar', $event)"
|
||||
>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="banner">Banner</label>
|
||||
<input
|
||||
type="file"
|
||||
id="banner"
|
||||
accept="image/*"
|
||||
@change="onFilePicked('header', $event)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra-fields input-group">
|
||||
<label>
|
||||
Additional info
|
||||
<div class="sub-label">You can have up to {{ extraFieldMaxCount }} items displayed as a table on your profile</div>
|
||||
</label>
|
||||
<div
|
||||
v-for="(field, index) in form.fields_attributes"
|
||||
:key="index"
|
||||
class="extra-field"
|
||||
:class="{'error': !isValidExtraField(index)}"
|
||||
>
|
||||
<input v-model.trim="field.name" placeholder="Label">
|
||||
<input
|
||||
:value="field.value_source"
|
||||
@input="onExtraFieldUpdate(field, $event)"
|
||||
placeholder="Content"
|
||||
<div class="extra-fields input-group">
|
||||
<label>
|
||||
Additional info
|
||||
<div class="sub-label">You can have up to {{ extraFieldMaxCount }} items displayed as a table on your profile</div>
|
||||
</label>
|
||||
<div
|
||||
v-for="(field, index) in form.fields_attributes"
|
||||
:key="index"
|
||||
class="extra-field"
|
||||
:class="{'error': !isValidExtraField(index)}"
|
||||
>
|
||||
<a
|
||||
class="remove-extra-field"
|
||||
title="Remove item"
|
||||
@click="removeExtraField(index)"
|
||||
<input v-model.trim="field.name" placeholder="Label">
|
||||
<input
|
||||
:value="field.value_source"
|
||||
@input="onExtraFieldUpdate(field, $event)"
|
||||
placeholder="Content"
|
||||
>
|
||||
<a
|
||||
class="remove-extra-field"
|
||||
title="Remove item"
|
||||
@click="removeExtraField(index)"
|
||||
>
|
||||
<img :src="require('@/assets/feather/x-circle.svg')">
|
||||
</a>
|
||||
</div>
|
||||
<button
|
||||
v-if="form.fields_attributes.length <= extraFieldMaxCount"
|
||||
type="button"
|
||||
class="add-extra-field"
|
||||
@click="addExtraField()"
|
||||
>
|
||||
<img :src="require('@/assets/feather/x-circle.svg')">
|
||||
</a>
|
||||
<img :src="require('@/assets/feather/plus-circle.svg')">
|
||||
Add new item
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="form.fields_attributes.length <= extraFieldMaxCount"
|
||||
type="button"
|
||||
class="add-extra-field"
|
||||
@click="addExtraField()"
|
||||
>
|
||||
<img :src="require('@/assets/feather/plus-circle.svg')">
|
||||
Add new item
|
||||
<button type="submit" class="btn" :disabled="!isFormValid()">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<button type="submit" class="btn" :disabled="!isFormValid()">
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -93,7 +94,7 @@ import {
|
|||
updateProfile,
|
||||
} from "@/api/users"
|
||||
import ProfileCard from "@/components/ProfileCard.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
import { setupAutoResize } from "@/utils/autoresize"
|
||||
import { renderMarkdownLite } from "@/utils/markdown"
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<div class="content posts">
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<post-list :posts="posts" @load-next-page="loadNextPage"></post-list>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -13,7 +12,7 @@ import { $ref } from "vue/macros"
|
|||
|
||||
import { Post, getPublicTimeline } from "@/api/posts"
|
||||
import PostList from "@/components/PostList.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
||||
const { ensureAuthToken } = useCurrentUser()
|
||||
|
@ -30,6 +29,3 @@ async function loadNextPage(maxId: string) {
|
|||
posts.push(...nextPage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<div class="content search-result-group">
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<loader v-if="isLoading"></loader>
|
||||
<div v-if="!isLoading" class="search-message">
|
||||
<template v-if="errorMessage">{{ errorMessage }}</template>
|
||||
|
@ -20,9 +20,8 @@
|
|||
:key="post.id"
|
||||
></post>
|
||||
</div>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -36,7 +35,7 @@ import { Profile } from "@/api/users"
|
|||
import Loader from "@/components/Loader.vue"
|
||||
import Post from "@/components/Post.vue"
|
||||
import ProfileListItem from "@/components/ProfileListItem.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
||||
const route = useRoute()
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<template>
|
||||
<div id="main" v-if="profile && isLocalUser()">
|
||||
<div class="content">
|
||||
<sidebar-layout v-if="profile && isLocalUser()">
|
||||
<template #content>
|
||||
<component
|
||||
:is="isCurrentUser() ? SubscriptionSetup : Subscription"
|
||||
:profile="profile"
|
||||
></component>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -16,7 +15,7 @@ import { $, $ref } from "vue/macros"
|
|||
import { useRoute } from "vue-router"
|
||||
|
||||
import { getProfile, Profile } from "@/api/users"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import Subscription from "@/components/Subscription.vue"
|
||||
import SubscriptionSetup from "@/components/SubscriptionSetup.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<template>
|
||||
<div id="main">
|
||||
<div class="content posts">
|
||||
<sidebar-layout>
|
||||
<template #content>
|
||||
<post-list
|
||||
:posts="posts"
|
||||
@load-next-page="loadNextPage"
|
||||
></post-list>
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</div>
|
||||
</template>
|
||||
</sidebar-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -16,7 +15,7 @@ import { useRoute } from "vue-router"
|
|||
|
||||
import { Post, getTagTimeline } from "@/api/posts"
|
||||
import PostList from "@/components/PostList.vue"
|
||||
import Sidebar from "@/components/Sidebar.vue"
|
||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||
import { useCurrentUser } from "@/store/user"
|
||||
|
||||
const route = useRoute()
|
||||
|
|
Loading…
Reference in a new issue