Kaynağa Gözat

Change from select menu to side bar

sbkwgh 8 yıl önce
ebeveyn
işleme
2d404ae90c
2 değiştirilmiş dosya ile 279 ekleme ve 51 silme
  1. 276 49
      src/components/routes/Index.vue
  2. 3 2
      src/main.js

+ 276 - 49
src/components/routes/Index.vue

@@ -1,78 +1,305 @@
 <template>
 	<div class='route_container'>
-		<div class='h1'>Categories</div>
-		<div class='index_categories'>
-			<div
-				class='index_category'
-				@click='$router.push("/category/" + category.value.toLowerCase())'
-				v-for='category in $store.state.meta.categories'
-				v-if='category.value !== "ALL"'
-			>
-				<div class='index_category__name'>{{category.name}}</div>
-				<div>
-					<div class='index_category__latest_post'>
-						<template v-if='category.Threads && category.Threads.length'>
-							{{category.Threads[0].name | truncate(100) }}
-						</template>
-						<template v-else>
-							No threads yet
-						</template>
-					</div>
-					<div class='index_category__latest_post_date' v-if='category.Threads && category.Threads.length'>
-						{{category.Threads[0].createdAt | formatDate('time|date', ' - ')}}
-					</div>
-				</div>
+		<div class='thread_sorting'>
+			<div>
+				<select-options
+					:options='displayOptions'
+					class='thread_sorting__display'
+					v-model='selectedDisplayOption'
+				></select-options>
+				<select-options
+					:options='filterOptions'
+					v-model='selectedFilterOption'
+					class='thread_sorting__filter'
+				></select-options>
 			</div>
+			<button class='button' v-if='this.$store.state.username' @click='$router.push("/thread/new")'>Post new thread</button>
 		</div>
-		<div v-if='!$store.state.meta.categories.length'>
-			There are no categories to show, but you can add them on the admin page
+		<div class='threads_main'>
+			<div class='threads_main__side_bar'>
+				<div class='threads_main__side_bar__title'>
+					categories
+				</div>
+				<div
+					v-for='category in categories'
+					class='threads_main__side_bar__menu_item'
+					:class='{"threads_main__side_bar__menu_item--selected": category.value === selectedCategory}'
+					@click='selectedCategory = category.value'
+				>
+					<span class='threads_main__side_bar__menu_item__border'></span>
+					{{category.name}}
+				</div>
+			</div>
+			<table class='threads_main__threads' v-if='filteredThreads.length'>
+				<colgroup>
+					<col span="1" style="width: 50%;">
+					<col span="1" style="width: 22.5%;">
+					<col span="1" style="width: 22.5%;">
+					<col span="1" style="width: 5%;">
+				</colgroup>
+				<thead>
+					<tr class='thread thread--header'>
+						<th>Title</th>
+						<th>Latest post</th>
+						<th>Category</th>
+						<th>Replies</th>
+					</tr>
+				</thead>
+				<tbody>
+					<tr class='thread' v-for='thread in filteredThreads' @click='navigateToThread(thread.slug, thread.id)'>
+						<td>{{thread.name}}</td>
+						<td>
+							<div>{{thread.Posts[0].content | stripTags | truncate(100)}}</div>
+							<div>{{thread.Posts[0].createdAt | formatDate('time|date', ' - ') }}</div>
+						</td>
+						<td>{{thread.Category.name}}</td>
+						<td>{{thread.replies}}</td>
+					</tr>
+				</tbody>
+			</table>
+			<div v-else class='threads_main__threads thread--empty'>No threads or posts.</div>
 		</div>
 	</div>
 </template>
 
 <script>
+	import TabView from '../TabView'
+	import SelectOptions from '../SelectOptions'
+
+	import AjaxErrorHandler from '../../assets/js/errorHandler'
+
 	export default {
-		name: 'index'
+		name: 'index',
+		components: {
+			TabView,
+			SelectOptions
+		},
+		data () {
+			return {
+				filterOptions: [
+					{name: 'New', value: 'NEW'},
+					{name: 'Most active', value: 'MOST_ACTIVE'},
+					{name: 'No replies', value: 'NO_REPLIES'}
+				],
+				selectedFilterOption: 'NEW',
+
+				displayOptions: [
+					{ name: 'Threads', value: 'THREADS' },
+					{ name: 'Posts', value: 'POSTS' }
+				],
+				selectedDisplayOption: 'THREADS',
+
+				threads: []
+			}
+		},
+		computed: {
+			filteredThreads () {
+				var categories = {};
+				var filter = this.selectedFilterOption
+
+				this.$store.state.meta.categories.forEach(category => {
+					categories[category.value] = category.name;
+				});
+
+				return this.threads.filter(thread => {
+					return (thread.Category.value === this.selectedCategory) || (this.selectedCategory === 'ALL');
+				}).map(thread => {
+					var _thread = Object.assign({}, thread);
+					_thread.category = categories[thread.Category.value];
+
+					return _thread;
+				}).sort((a, b) => {
+					if(filter === 'NEW') {
+						let aDate = new Date(a.Posts[0].createdAt)
+						let bDate = new Date(b.Posts[0].createdAt)
+
+						return aDate - bDate;
+					} else if(filter === 'MOST_ACTIVE') {
+						return 0 //b.replies - a.replies;
+					}
+				}).filter(thread => {
+					if(filter === 'NO_REPLIES') {
+						return 0//!thread.replies;
+					} else {
+						return true;
+					}
+				});
+			},
+			categories () {
+				return this.$store.state.meta.categories
+			},
+			selectedCategory: {
+				set (val) {
+					this.$store.commit('setSelectedCategory', val)
+				},
+				get () {
+					return this.$store.state.category.selectedCategory
+				}
+			}
+		},
+		methods: {
+			navigateToThread (slug, id) {
+				this.$router.push('/thread/' + slug + '/' + id);
+			},
+			getThreads () {
+				this.axios
+					.get('/api/v1/category/' + this.selectedCategory)
+					.then(res => {
+						this.threads = res.data.Threads
+					})
+					.catch(AjaxErrorHandler(this.$store))
+			}
+		},
+		watch: {
+			selectedCategory (newValue) {
+				this.$router.push('/' + newValue.toLowerCase());
+			},
+			$route () {
+				this.selectedCategory = this.$route.path.split('/')[1].toUpperCase()
+				this.getThreads()
+			}
+		},
+		created () {
+			this.selectedCategory = this.$route.path.split('/')[1].toUpperCase()
+			this.getThreads()
+		}
 	}
 </script>
 
 <style lang='scss' scoped>
 	@import '../../assets/scss/variables.scss';
 
-	.index_categories {
+	.threads_main {
+		display: flex;
+	}
+
+	.thread_sorting {
+		margin-bottom: 1rem;
 		display: flex;
-		overflow-y: auto;
-		max-height: 100%;
-		flex-wrap: wrap;
 		justify-content: space-between;
-		padding: 0.5rem 0px;
+
+		@at-root #{&}__display {
+			padding-right: 0.5rem;
+			border-right: thin solid $color__gray--primary;
+			margin-right: 0.5rem;
+			width: 10rem;
+		}
 	}
-	.index_category {
-		background-color: rgba(76, 175, 80, 0.86);
-		width: calc(100% / 4 - 1rem);
-		height: 5rem;
-		margin: 0.5rem;
-		padding: 0.5rem;
-		cursor: pointer;
-		color: #fff;
-		transition: filter 0.2s, transform 0.2s;
+
+	.threads_main__side_bar {
+		width: 10rem;
+		border-right: thin solid $color__gray--primary;
+		margin-top: 0.5rem;
+
+		@at-root #{&}__title {
+			cursor: default;
+			font-weight: 500;
+			font-variant: small-caps;
+			font-size: 1.125rem;
+		}
+
+		@at-root #{&}__menu_item {
+			cursor: pointer;
+			margin-top: 0.5rem;
+			position: relative;
+
+			#{&}__border {
+				display: inline-block;
+				width: 0.2rem;
+				z-index: 1;
+				height: 100%;
+				position: absolute;
+				left: -0.75rem;
+				opacity: 0;
+				top: 0.1rem;
+				background-color: $color__gray--darkest;
+
+				transition: all 0.2s;
+			}
+
+			&:hover #{&}__border {
+				left: -0.5rem;
+				opacity: 1;
+			}
+
+			&:active #{&}__border {
+				filter: brightness(0.8);
+			}
+
+			#{&}--selected {
+				font-weight: 500;
+
+				.threads_main__side_bar__menu_item__border {
+					opacity: 1;
+					left: -0.5rem;
+
+					&:active {
+						filter: brightness(1);
+					}
+				}
+			}
+		}
+	}
+	.threads_main__threads {
+		border-collapse: collapse;
+		margin-top: 0.25rem;
+		width: calc(100% - 10rem);
+	}
+
+	.thread {
+		background-color: #fff;
+		padding: 0.5rem 0;
+		cursor: default;
+		text-align: left;
+		transition: background-color 0.2s;
 
 		&:hover {
-			filter: brightness(0.8) contrast(130%);
+			background-color: $color__lightgray--primary;
+		}
+
+		td, th {
+			padding: 0.3rem 0.5rem;
+			border-bottom: solid thin $color__lightgray--primary;
+		}
+
+		@at-root #{&}--header {
+			&:hover {
+				background-color: #fff;
+			}
+
+			th {
+				font-weight: 400;
+				padding-bottom: 0.25rem;
+				border-bottom: thin solid $color__lightgray--darkest;
+			}
 		}
-		&:active {
-			transform: scale(0.96);
+
+		@at-root #{&}--empty {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			padding-right: 5rem;
+			font-size: 2rem;
+			user-select: none;
+			cursor: default;
+			transition: none;
+
+			&:hover {
+				transition: none;
+				background-color: #fff;
+			}
 		}
 
-		@at-root #{&}__name {
-			@include text($font--role-default, 1.5rem);
+		@at-root #{&}__section {
+			padding: 0 0.5rem;
 		}
-		@at-root #{&}__latest_post {
-			@include text($font--role-default, 1rem);
+
+		@at-root #{&}__user {
+			display: inline-block;
 		}
-		@at-root #{&}__latest_post_date {
-			color: $color__gray--darker;
-			@include text($font--role-default, 1rem);
+		@at-root #{&}__date {
+			color: $color__text--secondary;
+			display: inline-block;
 		}
 	}
 </style>

+ 3 - 2
src/main.js

@@ -33,9 +33,10 @@ Vue.use(VueAxios, axios)
 
 const router = new VueRouter({
 	routes: [
-		{ path: '/', component: Index },
+		{ path: '/', redirect: '/all' },
+		{ path: '/:category', component: Index },
 		{ path: '/start', component: Start },
-		{ path: '/category/:category', component: Category },
+		//{ path: '/category/:category', component: Category },
 		{ path: '/thread/:slug/:id', component: Thread },
 		{ path: '/thread/:slug/:id/:post_id', component: Thread },
 		{ path: '/thread/new', component: ThreadNew },