Browse Source

feat(AdvancedTable): Toggle columns dropdown and default advanced search

Owen Diffey 3 years ago
parent
commit
7daac01892

+ 11 - 11
frontend/src/App.vue

@@ -805,7 +805,7 @@ img {
 		}
 
 		&[data-theme~="songActions"],
-		&[data-theme~="addToPlaylist"],
+		&[data-theme~="dropdown"],
 		&[data-theme~="search"],
 		&[data-theme~="stationSettings"] {
 			background-color: var(--dark-grey-2);
@@ -826,7 +826,7 @@ img {
 			}
 		}
 
-		&[data-theme~="addToPlaylist"] {
+		&[data-theme~="dropdown"] {
 			background-color: var(--dark-grey-2);
 			border: 0 !important;
 
@@ -853,7 +853,7 @@ img {
 
 	.tippy-box[data-placement^="top"] {
 		&[data-theme~="songActions"],
-		&[data-theme~="addToPlaylist"],
+		&[data-theme~="dropdown"],
 		&[data-theme~="search"] {
 			> .tippy-arrow::before {
 				border-top-color: var(--dark-grey-2);
@@ -863,7 +863,7 @@ img {
 
 	.tippy-box[data-placement^="bottom"] {
 		&[data-theme~="songActions"],
-		&[data-theme~="addToPlaylist"],
+		&[data-theme~="dropdown"],
 		&[data-theme~="search"],
 		&[data-theme~="stationSettings"] {
 			> .tippy-arrow::before {
@@ -874,7 +874,7 @@ img {
 
 	.tippy-box[data-placement^="left"] {
 		&[data-theme~="songActions"],
-		&[data-theme~="addToPlaylist"],
+		&[data-theme~="dropdown"],
 		&[data-theme~="search"] {
 			> .tippy-arrow::before {
 				border-left-color: var(--dark-grey-2);
@@ -884,7 +884,7 @@ img {
 
 	.tippy-box[data-placement^="right"] {
 		&[data-theme~="songActions"],
-		&[data-theme~="addToPlaylist"],
+		&[data-theme~="dropdown"],
 		&[data-theme~="search"] {
 			> .tippy-arrow::before {
 				border-right-color: var(--dark-grey-2);
@@ -977,7 +977,7 @@ img {
 
 .tippy-box[data-placement^="top"] {
 	&[data-theme~="songActions"],
-	&[data-theme~="addToPlaylist"],
+	&[data-theme~="dropdown"],
 	&[data-theme~="search"] {
 		> .tippy-arrow::before {
 			border-top-color: var(--white);
@@ -990,7 +990,7 @@ img {
 
 .tippy-box[data-placement^="bottom"] {
 	&[data-theme~="songActions"],
-	&[data-theme~="addToPlaylist"],
+	&[data-theme~="dropdown"],
 	&[data-theme~="stationSettings"],
 	&[data-theme~="search"] {
 		> .tippy-arrow::before {
@@ -1004,7 +1004,7 @@ img {
 
 .tippy-box[data-placement^="left"] {
 	&[data-theme~="songActions"],
-	&[data-theme~="addToPlaylist"],
+	&[data-theme~="dropdown"],
 	&[data-theme~="search"] {
 		> .tippy-arrow::before {
 			border-left-color: var(--white);
@@ -1017,7 +1017,7 @@ img {
 
 .tippy-box[data-placement^="right"] {
 	&[data-theme~="songActions"],
-	&[data-theme~="addToPlaylist"],
+	&[data-theme~="dropdown"],
 	&[data-theme~="search"] {
 		> .tippy-arrow::before {
 			border-right-color: var(--white);
@@ -1038,7 +1038,7 @@ img {
 	}
 }
 
-.tippy-box[data-theme~="addToPlaylist"] {
+.tippy-box[data-theme~="dropdown"] {
 	font-size: 15px;
 	padding: 0;
 	border: 1px solid var(--light-grey-3);

+ 1 - 1
frontend/src/components/AddToPlaylistDropdown.vue

@@ -4,7 +4,7 @@
 		:touch="true"
 		:interactive="true"
 		:placement="placement"
-		theme="addToPlaylist"
+		theme="dropdown"
 		ref="dropdown"
 		trigger="click"
 		append-to="parent"

+ 333 - 280
frontend/src/components/AdvancedTable.vue

@@ -1,284 +1,323 @@
 <template>
-	<div>
-		<div>
-			<button
-				v-for="column in hidableSortedColumns"
-				:key="column.name"
-				class="button"
-				@click="toggleColumnVisibility(column)"
-			>
-				{{
-					`${
-						this.shownColumns.indexOf(column.name) !== -1
-							? "Hide"
-							: "Show"
-					} ${column.name} column`
-				}}
-			</button>
-		</div>
-		<div class="table-outer-container">
-			<div class="table-container">
-				<div class="table-header">
-					<div class="table-buttons">
-						<tippy
-							:touch="true"
-							:interactive="true"
-							placement="bottom"
-							theme="search"
-							ref="search"
-							trigger="click"
+	<div class="table-outer-container">
+		<div class="table-container">
+			<div class="table-header">
+				<tippy
+					v-if="filters.length > 0"
+					:touch="true"
+					:interactive="true"
+					placement="bottom"
+					theme="search"
+					ref="search"
+					trigger="click"
+				>
+					<a class="button is-info" @click.prevent="true">
+						<i class="material-icons icon-with-button">search</i>
+						Search
+					</a>
+
+					<template #content>
+						<div
+							v-for="(query, index) in advancedQuery"
+							:key="`query-${index}`"
+							class="advanced-query"
 						>
-							<a class="button is-info" @click.prevent="true">
-								<i class="material-icons icon-with-button"
-									>search</i
+							<div class="control select">
+								<select v-model="query.filter">
+									<option
+										v-for="f in filters"
+										:key="f.name"
+										:value="f"
+									>
+										{{ f.displayName }}
+									</option>
+								</select>
+							</div>
+							<div class="control select">
+								<select
+									v-if="query.filter.type"
+									v-model="query.type"
 								>
-								Search
-							</a>
-
-							<template #content>
-								<div
-									v-for="(query, index) in advancedQuery"
-									:key="`query-${index}`"
-									class="advanced-query"
+									<option
+										v-for="type in filterTypes"
+										:key="type"
+										:value="type"
+									>
+										{{ type }}
+									</option>
+								</select>
+							</div>
+							<p class="control is-expanded">
+								<input
+									v-if="query.type === 'regex'"
+									v-model="query.regex"
+									class="input"
+									type="text"
+									placeholder="Search value"
+									@keyup.enter="changeFilter()"
+								/>
+							</p>
+							<div class="control">
+								<button
+									class="button material-icons is-success"
+									@click="addQueryItem()"
 								>
-									<div class="control select">
-										<select v-model="query.filter">
-											<option
-												v-for="f in filters"
-												:key="f.name"
-												:value="f"
-											>
-												{{ f.displayName }}
-											</option>
-										</select>
-									</div>
-									<div class="control select">
-										<select
-											v-if="query.filter.type"
-											v-model="query.type"
-										>
-											<option
-												v-for="type in filterTypes"
-												:key="type"
-												:value="type"
-											>
-												{{ type }}
-											</option>
-										</select>
-									</div>
-									<p class="control is-expanded">
+									control_point
+								</button>
+							</div>
+							<div
+								v-if="advancedQuery.length > 1"
+								class="control"
+							>
+								<button
+									class="button material-icons is-danger"
+									@click="removeQueryItem(index)"
+								>
+									remove_circle_outline
+								</button>
+							</div>
+						</div>
+						<a class="button is-info" @click="changeFilter()">
+							<i class="material-icons icon-with-button"
+								>search</i
+							>
+							Search
+						</a>
+					</template>
+				</tippy>
+				<tippy
+					v-if="hidableSortedColumns.length > 0"
+					:touch="true"
+					:interactive="true"
+					placement="bottom"
+					theme="dropdown"
+					ref="toggleColumns"
+					trigger="click"
+				>
+					<a class="button is-info" @click.prevent="true">
+						<i class="material-icons icon-with-button"
+							>visibility</i
+						>
+						Toggle Columns
+					</a>
+
+					<template #content>
+						<div class="nav-dropdown-items">
+							<button
+								v-for="(column, index) in hidableSortedColumns"
+								:key="column.name"
+								class="nav-item"
+								@click.prevent="toggleColumnVisibility(column)"
+								:title="column.displayName"
+							>
+								<p class="control is-expanded checkbox-control">
+									<label class="switch">
 										<input
-											v-if="query.type === 'regex'"
-											v-model="query.regex"
-											class="input"
-											type="text"
-											placeholder="Search value"
-											@keyup.enter="changeFilter()"
+											type="checkbox"
+											:id="index"
+											:checked="
+												shownColumns.indexOf(
+													column.name
+												) !== -1
+											"
+											@click="
+												toggleColumnVisibility(column)
+											"
 										/>
-									</p>
-									<div class="control">
-										<button
-											class="
-												button
-												material-icons
-												is-success
+										<span class="slider round"></span>
+									</label>
+									<label :for="index">
+										<span></span>
+										<p>{{ column.displayName }}</p>
+									</label>
+								</p>
+							</button>
+						</div>
+					</template>
+				</tippy>
+			</div>
+			<table class="table">
+				<thead>
+					<draggable
+						item-key="name"
+						v-model="orderedColumns"
+						v-bind="columnDragOptions"
+						tag="tr"
+						draggable=".item-draggable"
+					>
+						<template #item="{ element: column }">
+							<th
+								:class="{
+									sortable: column.sortable,
+									'item-draggable': column.draggable
+								}"
+								:style="{
+									minWidth: column.minWidth,
+									width: column.width,
+									maxWidth: column.maxWidth
+								}"
+								v-if="shownColumns.indexOf(column.name) !== -1"
+							>
+								<div>
+									<span>
+										{{ column.displayName }}
+									</span>
+									<span
+										v-if="column.sortable"
+										:content="`Sort by ${column.displayName}`"
+										v-tippy
+									>
+										<span
+											v-if="!sort[column.sortProperty]"
+											class="material-icons"
+											@click="changeSort(column)"
+										>
+											unfold_more
+										</span>
+										<span
+											v-if="
+												sort[column.sortProperty] ===
+												'ascending'
 											"
-											@click="addQueryItem()"
+											class="material-icons active"
+											@click="changeSort(column)"
 										>
-											control_point
-										</button>
-									</div>
-									<div
-										v-if="advancedQuery.length > 1"
-										class="control"
-									>
-										<button
-											class="
-												button
-												material-icons
-												is-danger
+											expand_less
+										</span>
+										<span
+											v-if="
+												sort[column.sortProperty] ===
+												'descending'
 											"
-											@click="removeQueryItem(index)"
+											class="material-icons active"
+											@click="changeSort(column)"
 										>
-											remove_circle_outline
-										</button>
-									</div>
-								</div>
-								<a
-									class="button is-info"
-									@click="changeFilter()"
-								>
-									<i class="material-icons icon-with-button"
-										>search</i
+											expand_more
+										</span>
+									</span>
+									<span
+										v-if="column.draggable"
+										content="Toggle Pinned Column"
+										v-tippy
 									>
-									Search
-								</a>
-							</template>
-						</tippy>
-					</div>
-				</div>
-				<table class="table">
-					<thead>
-						<draggable
-							item-key="name"
-							v-model="orderedColumns"
-							v-bind="columnDragOptions"
-							tag="tr"
-							draggable=".item-draggable"
-						>
-							<template #item="{ element: column }">
-								<th
-									:class="{
-										sortable: column.sortable,
-										'item-draggable': column.draggable
-									}"
-									:style="{
-										minWidth: column.minWidth,
-										width: column.width,
-										maxWidth: column.maxWidth
-									}"
-									v-if="
-										shownColumns.indexOf(column.name) !== -1
-									"
-								>
-									<div>
-										<span>
-											{{ column.displayName }}
+										<span
+											:class="{
+												'material-icons': true,
+												active: false
+											}"
+										>
+											push_pin
 										</span>
+									</span>
+									<span
+										v-if="column.hidable"
+										content="Hide Column"
+										v-tippy
+									>
 										<span
-											v-if="column.sortable"
-											:content="`Sort by ${column.displayName}`"
-											v-tippy
+											@click="
+												toggleColumnVisibility(column)
+											"
+											class="material-icons"
 										>
-											<span
-												v-if="
-													!sort[column.sortProperty]
-												"
-												class="material-icons"
-												@click="changeSort(column)"
-											>
-												unfold_more
-											</span>
-											<span
-												v-if="
-													sort[
-														column.sortProperty
-													] === 'ascending'
-												"
-												class="material-icons"
-												@click="changeSort(column)"
-											>
-												expand_less
-											</span>
-											<span
-												v-if="
-													sort[
-														column.sortProperty
-													] === 'descending'
-												"
-												class="material-icons"
-												@click="changeSort(column)"
-											>
-												expand_more
-											</span>
+											visibility_off
 										</span>
-									</div>
-								</th>
-							</template>
-						</draggable>
-					</thead>
-					<tbody>
-						<tr
-							v-for="(item, itemIndex) in data"
-							:key="item._id"
-							:class="{
-								selected: item.selected,
-								highlighted: item.highlighted
-							}"
-							@click="clickItem(itemIndex, $event)"
+									</span>
+								</div>
+							</th>
+						</template>
+					</draggable>
+				</thead>
+				<tbody>
+					<tr
+						v-for="(item, itemIndex) in data"
+						:key="item._id"
+						:class="{
+							selected: item.selected,
+							highlighted: item.highlighted
+						}"
+						@click="clickItem(itemIndex, $event)"
+					>
+						<td
+							v-for="column in sortedFilteredColumns"
+							:key="`${item._id}-${column.name}`"
 						>
-							<td
-								v-for="column in sortedFilteredColumns"
-								:key="`${item._id}-${column.name}`"
+							<slot
+								:name="`column-${column.name}`"
+								:item="item"
+								v-if="
+									column.properties.every(
+										property => item[property] !== undefined
+									)
+								"
+							></slot>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+			<div class="table-footer">
+				<div class="page-controls">
+					<button
+						:class="{ disabled: page === 1 }"
+						class="button is-primary material-icons"
+						:disabled="page === 1"
+						@click="changePage(1)"
+						content="First Page"
+						v-tippy
+					>
+						skip_previous
+					</button>
+					<button
+						:class="{ disabled: page === 1 }"
+						class="button is-primary material-icons"
+						:disabled="page === 1"
+						@click="changePage(page - 1)"
+						content="Previous Page"
+						v-tippy
+					>
+						fast_rewind
+					</button>
+
+					<p>Page {{ page }} / {{ lastPage }}</p>
+
+					<button
+						:class="{ disabled: page === lastPage }"
+						class="button is-primary material-icons"
+						:disabled="page === lastPage"
+						@click="changePage(page + 1)"
+						content="Next Page"
+						v-tippy
+					>
+						fast_forward
+					</button>
+					<button
+						:class="{ disabled: page === lastPage }"
+						class="button is-primary material-icons"
+						:disabled="page === lastPage"
+						@click="changePage(lastPage)"
+						content="Last Page"
+						v-tippy
+					>
+						skip_next
+					</button>
+				</div>
+				<div class="page-size">
+					<div class="control">
+						<label class="label">Items per page</label>
+						<p class="control select">
+							<select
+								v-model.number="pageSize"
+								@change="changePageSize()"
 							>
-								<slot
-									:name="`column-${column.name}`"
-									:item="item"
-									v-if="
-										column.properties.every(
-											property =>
-												item[property] !== undefined
-										)
-									"
-								></slot>
-							</td>
-						</tr>
-					</tbody>
-				</table>
-				<div class="table-footer">
-					<div class="page-controls">
-						<button
-							:class="{ disabled: page === 1 }"
-							class="button is-primary material-icons"
-							:disabled="page === 1"
-							@click="changePage(1)"
-							content="First Page"
-							v-tippy
-						>
-							skip_previous
-						</button>
-						<button
-							:class="{ disabled: page === 1 }"
-							class="button is-primary material-icons"
-							:disabled="page === 1"
-							@click="changePage(page - 1)"
-							content="Previous Page"
-							v-tippy
-						>
-							fast_rewind
-						</button>
-
-						<p>Page {{ page }} / {{ lastPage }}</p>
-
-						<button
-							:class="{ disabled: page === lastPage }"
-							class="button is-primary material-icons"
-							:disabled="page === lastPage"
-							@click="changePage(page + 1)"
-							content="Next Page"
-							v-tippy
-						>
-							fast_forward
-						</button>
-						<button
-							:class="{ disabled: page === lastPage }"
-							class="button is-primary material-icons"
-							:disabled="page === lastPage"
-							@click="changePage(lastPage)"
-							content="Last Page"
-							v-tippy
-						>
-							skip_next
-						</button>
-					</div>
-					<div class="page-size">
-						<div class="control">
-							<label class="label">Items per page</label>
-							<p class="control select">
-								<select
-									v-model.number="pageSize"
-									@change="changePageSize()"
-								>
-									<option value="10">10</option>
-									<option value="25">25</option>
-									<option value="50">50</option>
-									<option value="100">100</option>
-									<option value="250">250</option>
-									<option value="500">500</option>
-									<option value="1000">1000</option>
-								</select>
-							</p>
-						</div>
+								<option value="10">10</option>
+								<option value="25">25</option>
+								<option value="50">50</option>
+								<option value="100">100</option>
+								<option value="250">250</option>
+								<option value="500">500</option>
+								<option value="1000">1000</option>
+							</select>
+						</p>
 					</div>
 				</div>
 			</div>
@@ -337,13 +376,7 @@ export default {
 					fallbackTolerance: 50
 				};
 			},
-			advancedQuery: [
-				{
-					regex: "",
-					filter: {},
-					type: ""
-				}
-			]
+			advancedQuery: []
 		};
 	},
 	computed: {
@@ -400,6 +433,13 @@ export default {
 		const pageSize = parseInt(localStorage.getItem("adminPageSize"));
 		if (!Number.isNaN(pageSize)) this.pageSize = pageSize;
 
+		if (this.filters.length > 0)
+			this.advancedQuery.push({
+				regex: "",
+				filter: this.filters[0],
+				type: this.filters[0].type
+			});
+
 		ws.onConnect(this.init);
 	},
 	methods: {
@@ -519,11 +559,12 @@ export default {
 			this.data[itemIndex].highlighted = true;
 		},
 		addQueryItem() {
-			this.advancedQuery.push({
-				regex: "",
-				filter: {},
-				type: ""
-			});
+			if (this.filters.length > 0)
+				this.advancedQuery.push({
+					regex: "",
+					filter: this.filters[0],
+					type: this.filters[0].type
+				});
 		},
 		removeQueryItem(index) {
 			this.advancedQuery.splice(index, 1);
@@ -595,15 +636,27 @@ export default {
 							white-space: nowrap;
 
 							& > span {
+								margin-left: 5px;
+
+								&:first-child {
+									margin-left: 0;
+									margin-right: auto;
+								}
+
 								& > .material-icons {
 									font-size: 22px;
 									position: relative;
 									top: 6px;
 									cursor: pointer;
-								}
 
-								&:first-child {
-									margin-right: auto;
+									&.active {
+										color: var(--primary-color);
+									}
+
+									&:hover,
+									&:focus {
+										filter: brightness(90%);
+									}
 								}
 							}
 						}
@@ -665,7 +718,7 @@ export default {
 		background-color: var(--white);
 	}
 
-	.table-header .table-buttons > span > .button {
+	.table-header > span > .button {
 		margin: 5px;
 	}
 

+ 1 - 1
frontend/src/components/modals/ManageStation/Tabs/Settings.vue

@@ -560,7 +560,7 @@ export default {
 		display: flex;
 		flex-direction: column;
 
-		* >>> .tippy-box[data-theme~="addToPlaylist"] .tippy-content > span {
+		* >>> .tippy-box[data-theme~="dropdown"] .tippy-content > span {
 			max-width: 150px !important;
 		}