web/satellite: My Access Table style changes and search functionality (#4767)
Styled Access Grant Table according to new flow UX Added search functionality to My Access Table. Separated search styles into 2 components Co-authored-by: Moby von Briesen <mobyvb@gmail.com> Co-authored-by: hovex023 <97616907+hovex023@users.noreply.github.com>
This commit is contained in:
parent
7a2d2a36ca
commit
2b0016af62
@ -1,6 +1,5 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="access-grants">
|
||||
<div v-if="!isNewAccessGrantFlow" class="access-grants__title-area">
|
||||
@ -104,50 +103,102 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
|
||||
<div v-if="accessGrantsList.length && !areGrantsFetching" class="access-grants-items">
|
||||
<SortAccessGrantsHeader :on-header-click-callback="onHeaderSectionClickCallback" />
|
||||
<div class="access-grants-items__content">
|
||||
<VList
|
||||
:data-set="accessGrantsList"
|
||||
:item-component="itemComponent"
|
||||
:on-item-click="toggleSelection"
|
||||
<div v-if="isNewAccessGrantFlow">
|
||||
<div class="access-grants__header-container">
|
||||
<h3 class="access-grants__header-container__title">My Accesses</h3>
|
||||
<div class="access-grants__header-container__divider" />
|
||||
<VHeader
|
||||
class="access-header-component"
|
||||
placeholder="Accesses"
|
||||
:search="fetch"
|
||||
style-type="access"
|
||||
/>
|
||||
</div>
|
||||
<VPagination
|
||||
v-if="totalPageCount > 1"
|
||||
ref="pagination"
|
||||
class="pagination-area"
|
||||
:total-page-count="totalPageCount"
|
||||
:on-page-click-callback="onPageClick"
|
||||
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
|
||||
<div v-if="accessGrantsList.length && !areGrantsFetching" class="access-grants-items2">
|
||||
<SortAccessGrantsHeader2
|
||||
:on-header-click-callback="onHeaderSectionClickCallback"
|
||||
/>
|
||||
<div class="access-grants-items2__content">
|
||||
<VList
|
||||
:data-set="accessGrantsList"
|
||||
:item-component="itemComponent2"
|
||||
/>
|
||||
</div>
|
||||
<div class="access-grants-items2__footer">
|
||||
<span class="access-grants-items2__footer__total-accesses">
|
||||
{{ accessGrantsList.length }} Access Grants
|
||||
</span>
|
||||
<VPagination
|
||||
v-if="totalPageCount > 1"
|
||||
ref="pagination"
|
||||
class="access-grants-items2__footer__pagination-area"
|
||||
:total-page-count="totalPageCount"
|
||||
:on-page-click-callback="onPageClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!accessGrantsList.length && !areGrantsFetching"
|
||||
class="access-grants-items2__empty-state"
|
||||
>
|
||||
<span class="access-grants-items2__empty-state__text">
|
||||
No Results Found
|
||||
</span>
|
||||
</div>
|
||||
<ConfirmDeletePopup
|
||||
v-if="isDeleteClicked"
|
||||
@close="onClearSelection"
|
||||
@reset-pagination="resetPagination"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!isNewAccessGrantFlow">
|
||||
<VLoader v-if="areGrantsFetching" width="100px" height="100px" class="grants-loader" />
|
||||
<div v-if="accessGrantsList.length && !areGrantsFetching" class="access-grants-items">
|
||||
<SortAccessGrantsHeader :on-header-click-callback="onHeaderSectionClickCallback" />
|
||||
<div class="access-grants-items__content">
|
||||
<VList
|
||||
:data-set="accessGrantsList"
|
||||
:item-component="itemComponent"
|
||||
:on-item-click="toggleSelection"
|
||||
/>
|
||||
</div>
|
||||
<VPagination
|
||||
v-if="totalPageCount > 1"
|
||||
ref="pagination"
|
||||
class="pagination-area"
|
||||
:total-page-count="totalPageCount"
|
||||
:on-page-click-callback="onPageClick"
|
||||
/>
|
||||
</div>
|
||||
<EmptyState v-if="!accessGrantsList.length && !areGrantsFetching" />
|
||||
<ConfirmDeletePopup
|
||||
v-if="isDeleteClicked"
|
||||
@close="onClearSelection"
|
||||
@reset-pagination="resetPagination"
|
||||
/>
|
||||
</div>
|
||||
<EmptyState v-if="!accessGrantsList.length && !areGrantsFetching" />
|
||||
<ConfirmDeletePopup
|
||||
v-if="isDeleteClicked"
|
||||
@close="onClearSelection"
|
||||
@reset-pagination="resetPagination"
|
||||
/>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { MetaUtils } from '@/utils/meta';
|
||||
|
||||
import AccessGrantsItem from '@/components/accessGrants/AccessGrantsItem.vue';
|
||||
import AccessGrantsItem2 from '@/components/accessGrants/AccessGrantsItem2.vue';
|
||||
import ConfirmDeletePopup from '@/components/accessGrants/ConfirmDeletePopup.vue';
|
||||
import EmptyState from '@/components/accessGrants/EmptyState.vue';
|
||||
import SortAccessGrantsHeader from '@/components/accessGrants/SortingHeader.vue';
|
||||
import SortAccessGrantsHeader2 from '@/components/accessGrants/SortingHeader2.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
import VList from '@/components/common/VList.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
import VHeader from '@/components/common/VHeader.vue';
|
||||
import VPagination from '@/components/common/VPagination.vue';
|
||||
import AccessGrantsIcon from '@/../static/images/accessGrants/accessGrantsIcon.svg';
|
||||
import CLIIcon from '@/../static/images/accessGrants/cli.svg';
|
||||
import S3Icon from '@/../static/images/accessGrants/s3.svg';
|
||||
|
||||
import { RouteConfig } from '@/router';
|
||||
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
|
||||
import { AccessGrant, AccessGrantsOrderBy } from '@/types/accessGrants';
|
||||
@ -159,12 +210,11 @@ const {
|
||||
CLEAR_SELECTION,
|
||||
SET_SORT_BY,
|
||||
SET_SORT_DIRECTION,
|
||||
SET_SEARCH_QUERY,
|
||||
} = ACCESS_GRANTS_ACTIONS;
|
||||
|
||||
declare interface ResetPagination {
|
||||
resetPageIndex(): void;
|
||||
}
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
@ -173,23 +223,19 @@ declare interface ResetPagination {
|
||||
EmptyState,
|
||||
S3Icon,
|
||||
SortAccessGrantsHeader,
|
||||
SortAccessGrantsHeader2,
|
||||
VList,
|
||||
VPagination,
|
||||
VButton,
|
||||
ConfirmDeletePopup,
|
||||
VLoader,
|
||||
VHeader,
|
||||
},
|
||||
})
|
||||
export default class AccessGrants extends Vue {
|
||||
private FIRST_PAGE = 1;
|
||||
|
||||
/**
|
||||
* Indicates if delete confirmation state should appear.
|
||||
*/
|
||||
private isDeleteClicked = false;
|
||||
|
||||
public areGrantsFetching = true;
|
||||
|
||||
public $refs!: {
|
||||
pagination: HTMLElement & ResetPagination;
|
||||
};
|
||||
@ -207,13 +253,11 @@ export default class AccessGrants extends Vue {
|
||||
public async mounted(): Promise<void> {
|
||||
try {
|
||||
await this.$store.dispatch(FETCH, this.FIRST_PAGE);
|
||||
|
||||
this.areGrantsFetching = false;
|
||||
} catch (error) {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook before component destruction.
|
||||
* Clears existing access grants selection.
|
||||
@ -221,7 +265,6 @@ export default class AccessGrants extends Vue {
|
||||
public beforeDestroy(): void {
|
||||
this.onClearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles access grant selection.
|
||||
* @param accessGrant
|
||||
@ -229,7 +272,6 @@ export default class AccessGrants extends Vue {
|
||||
public async toggleSelection(accessGrant: AccessGrant): Promise<void> {
|
||||
await this.$store.dispatch(TOGGLE_SELECTION, accessGrant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches access grants page by clicked index.
|
||||
* @param index
|
||||
@ -241,7 +283,6 @@ export default class AccessGrants extends Vue {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for sorting.
|
||||
* @param sortBy
|
||||
@ -255,12 +296,10 @@ export default class AccessGrants extends Vue {
|
||||
} catch (error) {
|
||||
await this.$notify.error(`Unable to fetch Access Grants. ${error.message}`);
|
||||
}
|
||||
|
||||
if (this.totalPageCount > 1) {
|
||||
this.resetPagination();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets pagination to default state.
|
||||
*/
|
||||
@ -269,21 +308,18 @@ export default class AccessGrants extends Vue {
|
||||
this.$refs.pagination.resetPageIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts create access grant flow.
|
||||
*/
|
||||
public onCreateClick(): void {
|
||||
this.$router.push(RouteConfig.AccessGrants.with(RouteConfig.CreateAccessGrant).with(RouteConfig.NameStep).path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on button click login for deleting access grant process.
|
||||
*/
|
||||
public onDeleteClick(): void {
|
||||
this.isDeleteClicked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears access grants selection.
|
||||
*/
|
||||
@ -291,28 +327,39 @@ export default class AccessGrants extends Vue {
|
||||
await this.$store.dispatch(CLEAR_SELECTION);
|
||||
this.isDeleteClicked = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns delete access grants button label.
|
||||
* Fetches Access records by name depending on search query.
|
||||
*/
|
||||
public async fetch(searchQuery: string): Promise<void> {
|
||||
await this.$store.dispatch(SET_SEARCH_QUERY, searchQuery);
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(FETCH, 1);
|
||||
} catch (error) {
|
||||
await this.$notify.error(`Unable to fetch accesses: ${error.message}`);
|
||||
}
|
||||
}
|
||||
public get deleteButtonLabel(): string {
|
||||
return `Remove Selected (${this.selectedAccessGrantsAmount})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access grants pages count from store.
|
||||
*/
|
||||
public get totalPageCount(): number {
|
||||
return this.$store.state.accessGrantsModule.page.pageCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns AccessGrant item component.
|
||||
*/
|
||||
public get itemComponent(): typeof AccessGrantsItem {
|
||||
return AccessGrantsItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns AccessGrant2 item component.
|
||||
*/
|
||||
public get itemComponent2(): typeof AccessGrantsItem2 {
|
||||
return AccessGrantsItem2;
|
||||
}
|
||||
/**
|
||||
* Returns access grants from store.
|
||||
*/
|
||||
@ -326,9 +373,9 @@ export default class AccessGrants extends Vue {
|
||||
public get selectedAccessGrantsAmount(): number {
|
||||
return this.$store.state.accessGrantsModule.selectedAccessGrantsIds.length;
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@mixin grant-flow-card {
|
||||
display: inline-block;
|
||||
@ -433,9 +480,85 @@ export default class AccessGrants extends Vue {
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.access-grants-items2 {
|
||||
position: relative;
|
||||
|
||||
&__content {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 80px;
|
||||
width: 100%;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
&__total-accesses {
|
||||
height: 20px;
|
||||
padding-left: 20px;
|
||||
margin-left: 18px;
|
||||
color: #2c353a;
|
||||
}
|
||||
|
||||
&__pagination-area {
|
||||
padding-right: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty-state {
|
||||
height: 75px;
|
||||
width: auto;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
margin-top: 10px;
|
||||
border: 1px solid #dadfe7;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
&__text {
|
||||
font-family: sans-serif;
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
margin: auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.access-grants__header-container {
|
||||
|
||||
&__header-container {
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&__divider {
|
||||
height: 1px;
|
||||
width: auto;
|
||||
background-color: #dadfe7;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__access-header-component {
|
||||
height: 55px !important;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grants-loader {
|
||||
margin-top: 50px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@ -112,4 +112,5 @@ export default class AccessGrantsItem extends Vue {
|
||||
.date-item-container {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
177
web/satellite/src/components/accessGrants/AccessGrantsItem2.vue
Normal file
177
web/satellite/src/components/accessGrants/AccessGrantsItem2.vue
Normal file
@ -0,0 +1,177 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="grants-item-container">
|
||||
<div class="grants-item-container__common-info">
|
||||
<div class="name-container" :title="itemData.name">
|
||||
<p class="name">{{ itemData.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grants-item-container__common-info date-item-container">
|
||||
<p class="date">{{ itemData.localDate() }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grants-item-container__common-info menu-item-container">
|
||||
<p class="ellipses" @click="togglePopupVisibility">...</p>
|
||||
<div
|
||||
v-if="popupVisible"
|
||||
v-click-outside="popupVisible"
|
||||
class="popup-menu"
|
||||
>
|
||||
<p class="popup-menu__popup-details">
|
||||
See Details
|
||||
</p>
|
||||
<div class="popup-menu__popup-divider" />
|
||||
<p class="popup-menu__popup-delete">
|
||||
Delete Access
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { AccessGrant } from '@/types/accessGrants';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
},
|
||||
})
|
||||
export default class AccessGrantsItem extends Vue {
|
||||
@Prop({ default: new AccessGrant('', '', new Date(), '') })
|
||||
private readonly itemData: AccessGrant;
|
||||
private popupVisible = false;
|
||||
|
||||
public togglePopupVisibility(): void {
|
||||
this.popupVisible = !this.popupVisible;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@mixin popup-menu-button {
|
||||
padding: 0 15px;
|
||||
height: 50%;
|
||||
line-height: 55px;
|
||||
text-align: left;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
color: #1b2533;
|
||||
transition: 100ms;
|
||||
}
|
||||
|
||||
.grants-item-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
height: 83px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
&__common-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
margin-left: 28px;
|
||||
min-width: 21px;
|
||||
min-height: 21px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #1b2533;
|
||||
|
||||
&__image {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.name-container {
|
||||
max-width: calc(100% - 131px);
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #354049;
|
||||
margin-left: 38px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #354049;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ellipses {
|
||||
margin: 0 auto 20px;
|
||||
font-size: 30px;
|
||||
font-weight: 1000;
|
||||
color: #7c8794;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.popup-menu {
|
||||
width: 160px;
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
right: 70px;
|
||||
bottom: -90px;
|
||||
z-index: 1;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 20px 34px rgb(10 27 44 / 28%);
|
||||
|
||||
&__popup-details {
|
||||
@include popup-menu-button;
|
||||
|
||||
border-radius: 10px 10px 0 0;
|
||||
|
||||
&:hover {
|
||||
background-color: #354049;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&__popup-divider {
|
||||
height: 1px;
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
&__popup-delete {
|
||||
@include popup-menu-button;
|
||||
|
||||
border-radius: 0 0 10px 10px;
|
||||
|
||||
&:hover {
|
||||
background-color: #b53737;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-item-container {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.menu-item-container {
|
||||
width: 10%;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
120
web/satellite/src/components/accessGrants/SortingHeader2.vue
Normal file
120
web/satellite/src/components/accessGrants/SortingHeader2.vue
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="sort-header-container">
|
||||
<div class="sort-header-container__name-item" @click="onHeaderItemClick(AccessGrantsOrderBy.NAME)">
|
||||
<p class="sort-header-container__name-item__title">NAME</p>
|
||||
<VerticalArrows
|
||||
:is-active="areAccessGrantsSortedByName"
|
||||
:direction="getSortDirection"
|
||||
/>
|
||||
</div>
|
||||
<div class="sort-header-container__date-item" @click="onHeaderItemClick(AccessGrantsOrderBy.CREATED_AT)">
|
||||
<p class="sort-header-container__date-item__title creation-date">DATE CREATED</p>
|
||||
<VerticalArrows
|
||||
:is-active="!areAccessGrantsSortedByName"
|
||||
:direction="getSortDirection"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import VerticalArrows from '@/components/common/VerticalArrows.vue';
|
||||
|
||||
import { AccessGrantsOrderBy, OnHeaderClickCallback } from '@/types/accessGrants';
|
||||
import { SortDirection } from '@/types/common';
|
||||
|
||||
// @vue/component
|
||||
@Component({
|
||||
components: {
|
||||
VerticalArrows,
|
||||
},
|
||||
})
|
||||
export default class SortAccessGrantsHeader extends Vue {
|
||||
@Prop({default: () => new Promise(() => false)})
|
||||
private readonly onHeaderClickCallback: OnHeaderClickCallback;
|
||||
|
||||
public AccessGrantsOrderBy = AccessGrantsOrderBy;
|
||||
|
||||
public sortBy: AccessGrantsOrderBy = AccessGrantsOrderBy.NAME;
|
||||
public sortDirection: SortDirection = SortDirection.ASCENDING;
|
||||
|
||||
/**
|
||||
* Used for arrow styling.
|
||||
*/
|
||||
public get getSortDirection(): SortDirection {
|
||||
return this.sortDirection === SortDirection.DESCENDING ? SortDirection.ASCENDING : SortDirection.DESCENDING;
|
||||
}
|
||||
|
||||
public get areAccessGrantsSortedByName(): boolean {
|
||||
return this.sortBy === AccessGrantsOrderBy.NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sorting kind if different from current.
|
||||
* If same, changes sort direction.
|
||||
* @param sortBy
|
||||
*/
|
||||
public async onHeaderItemClick(sortBy: AccessGrantsOrderBy): Promise<void> {
|
||||
if (this.sortBy !== sortBy) {
|
||||
this.sortBy = sortBy;
|
||||
this.sortDirection = SortDirection.ASCENDING;
|
||||
|
||||
await this.onHeaderClickCallback(this.sortBy, this.sortDirection);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.sortDirection = this.sortDirection === SortDirection.DESCENDING ?
|
||||
SortDirection.ASCENDING
|
||||
: SortDirection.DESCENDING;
|
||||
|
||||
await this.onHeaderClickCallback(this.sortBy, this.sortDirection);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sort-header-container {
|
||||
display: flex;
|
||||
width: calc(100% - 32px);
|
||||
height: 40px;
|
||||
background-color: #fff;
|
||||
margin-top: 31px;
|
||||
padding: 16px 16px 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-bottom: 0;
|
||||
|
||||
&__name-item,
|
||||
&__date-item {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 16px;
|
||||
margin: 0 0 0 23px;
|
||||
color: #2a2a32;
|
||||
}
|
||||
|
||||
.creation-date {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__date-item {
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -6,11 +6,20 @@
|
||||
<div class="header-container__buttons-area">
|
||||
<slot />
|
||||
</div>
|
||||
<VSearch
|
||||
ref="search"
|
||||
:placeholder="placeholder"
|
||||
:search="search"
|
||||
/>
|
||||
<div v-if="styleType === 'common'">
|
||||
<VSearch
|
||||
ref="search"
|
||||
:placeholder="placeholder"
|
||||
:search="search"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="styleType === 'access'">
|
||||
<VSearchAlternateStyling
|
||||
ref="search"
|
||||
:placeholder="placeholder"
|
||||
:search="search"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -18,6 +27,7 @@
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import VSearch from '@/components/common/VSearch.vue';
|
||||
import VSearchAlternateStyling from '@/components/common/VSearchAlternateStyling.vue';
|
||||
|
||||
declare type searchCallback = (search: string) => Promise<void>;
|
||||
|
||||
@ -25,9 +35,12 @@ declare type searchCallback = (search: string) => Promise<void>;
|
||||
@Component({
|
||||
components: {
|
||||
VSearch,
|
||||
VSearchAlternateStyling,
|
||||
},
|
||||
})
|
||||
export default class VHeader extends Vue {
|
||||
@Prop({default: 'common'})
|
||||
private readonly styleType: string;
|
||||
@Prop({default: ''})
|
||||
private readonly placeholder: string;
|
||||
@Prop({default: function(): searchCallback {
|
||||
|
@ -54,7 +54,6 @@ export default class VSearch extends Vue {
|
||||
*/
|
||||
public onMouseEnter(): void {
|
||||
this.inputWidth = '540px';
|
||||
|
||||
this.$refs.input.focus();
|
||||
}
|
||||
|
||||
@ -103,8 +102,4 @@ export default class VSearch extends Vue {
|
||||
background-size: 22px 22px;
|
||||
background-position: top 16px right 16px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #afb7c1;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
class="access-search-input"
|
||||
:placeholder="`Search ${placeholder}`"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
@input="processSearchQuery"
|
||||
>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
declare type searchCallback = (search: string) => Promise<void>;
|
||||
|
||||
// @vue/component
|
||||
@Component
|
||||
export default class VSearch extends Vue {
|
||||
@Prop({default: ''})
|
||||
private readonly placeholder: string;
|
||||
@Prop({default: function(): searchCallback {
|
||||
return async function(_: string) {};
|
||||
}})
|
||||
private readonly search: searchCallback;
|
||||
private searchQuery = '';
|
||||
|
||||
public $refs!: {
|
||||
input: HTMLElement;
|
||||
};
|
||||
|
||||
public get searchString(): string {
|
||||
return this.searchQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears search query.
|
||||
*/
|
||||
public clearSearch(): void {
|
||||
this.searchQuery = '';
|
||||
this.processSearchQuery();
|
||||
}
|
||||
|
||||
public async processSearchQuery(): Promise<void> {
|
||||
await this.search(this.searchQuery);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.access-search-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding: 0 10px 0 50px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
border: 1px solid #d8dee3;
|
||||
border-radius: 10px;
|
||||
height: 56px;
|
||||
width: 250px;
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 16px;
|
||||
background-color: #fff;
|
||||
background-image: url('../../../static/images/common/search-gray.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 22px 22px;
|
||||
background-position: top 16px left 16px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #afb7c1;
|
||||
}
|
||||
</style>
|
@ -87,4 +87,4 @@ self.onmessage = async function (event) {
|
||||
default:
|
||||
self.postMessage(new Error('provided message event type is not supported'));
|
||||
}
|
||||
};
|
||||
};
|
BIN
web/satellite/static/images/common/search-gray.png
Normal file
BIN
web/satellite/static/images/common/search-gray.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 762 B |
@ -3,6 +3,9 @@
|
||||
exports[`HeaderComponent.vue renders correctly 1`] = `
|
||||
<div class="header-container">
|
||||
<div class="header-container__buttons-area"></div>
|
||||
<vsearch-stub placeholder="" search="[Function]"></vsearch-stub>
|
||||
<div>
|
||||
<vsearch-stub placeholder="" search="[Function]"></vsearch-stub>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
`;
|
||||
|
@ -5,7 +5,7 @@ exports[`BucketArea.vue renders correctly with bucket 1`] = `
|
||||
<div class="buckets-overflow">
|
||||
<div class="buckets-header">
|
||||
<p class="buckets-header__title">Usage per bucket</p>
|
||||
<vheader-stub placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
|
||||
<vheader-stub styletype="common" placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
|
||||
</div>
|
||||
<div class="buckets-container">
|
||||
<sortingheader-stub></sortingheader-stub>
|
||||
@ -22,7 +22,7 @@ exports[`BucketArea.vue renders correctly with pagination 1`] = `
|
||||
<div class="buckets-overflow">
|
||||
<div class="buckets-header">
|
||||
<p class="buckets-header__title">Usage per bucket</p>
|
||||
<vheader-stub placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
|
||||
<vheader-stub styletype="common" placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
|
||||
</div>
|
||||
<div class="buckets-container">
|
||||
<sortingheader-stub></sortingheader-stub>
|
||||
@ -48,7 +48,7 @@ exports[`BucketArea.vue renders correctly without search results 1`] = `
|
||||
<div class="buckets-overflow">
|
||||
<div class="buckets-header">
|
||||
<p class="buckets-header__title">Usage per bucket</p>
|
||||
<vheader-stub placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
|
||||
<vheader-stub styletype="common" placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
|
||||
</div>
|
||||
<!---->
|
||||
<div class="empty-search-result-area">
|
||||
|
@ -7,7 +7,7 @@ exports[`Team HeaderArea renders correctly 1`] = `
|
||||
<vinfo-stub title="" buttonlabel="" onbuttonclick="[Function]" class="team-header-container__title-area__info-button"></vinfo-stub>
|
||||
</div>
|
||||
<div class="team-header-container__wrapper">
|
||||
<vheader-stub placeholder="Team Members" search="[Function]">
|
||||
<vheader-stub styletype="common" placeholder="Team Members" search="[Function]">
|
||||
<div class="header-default-state">
|
||||
<vbutton-stub label="+ Add" width="122px" height="48px" fontsize="16px" borderradius="6px" onpress="[Function]" class="button"></vbutton-stub>
|
||||
</div>
|
||||
@ -28,7 +28,7 @@ exports[`Team HeaderArea renders correctly with 1 selected user and delete click
|
||||
<vinfo-stub title="" buttonlabel="" onbuttonclick="[Function]" class="team-header-container__title-area__info-button"></vinfo-stub>
|
||||
</div>
|
||||
<div class="team-header-container__wrapper">
|
||||
<vheader-stub placeholder="Team Members" search="[Function]">
|
||||
<vheader-stub styletype="common" placeholder="Team Members" search="[Function]">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="header-after-delete-click"><span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete <b>1</b> user?</span>
|
||||
@ -52,7 +52,7 @@ exports[`Team HeaderArea renders correctly with 2 selected users and delete clic
|
||||
<vinfo-stub title="" buttonlabel="" onbuttonclick="[Function]" class="team-header-container__title-area__info-button"></vinfo-stub>
|
||||
</div>
|
||||
<div class="team-header-container__wrapper">
|
||||
<vheader-stub placeholder="Team Members" search="[Function]">
|
||||
<vheader-stub styletype="common" placeholder="Team Members" search="[Function]">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="header-after-delete-click"><span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete <b>2</b> users?</span>
|
||||
@ -76,7 +76,7 @@ exports[`Team HeaderArea renders correctly with opened Add team member popup 1`]
|
||||
<vinfo-stub title="" buttonlabel="" onbuttonclick="[Function]" class="team-header-container__title-area__info-button"></vinfo-stub>
|
||||
</div>
|
||||
<div class="team-header-container__wrapper">
|
||||
<vheader-stub placeholder="Team Members" search="[Function]">
|
||||
<vheader-stub styletype="common" placeholder="Team Members" search="[Function]">
|
||||
<div class="header-default-state">
|
||||
<vbutton-stub label="+ Add" width="122px" height="48px" fontsize="16px" borderradius="6px" onpress="[Function]" class="button"></vbutton-stub>
|
||||
</div>
|
||||
@ -97,7 +97,7 @@ exports[`Team HeaderArea renders correctly with selected users 1`] = `
|
||||
<vinfo-stub title="" buttonlabel="" onbuttonclick="[Function]" class="team-header-container__title-area__info-button"></vinfo-stub>
|
||||
</div>
|
||||
<div class="team-header-container__wrapper">
|
||||
<vheader-stub placeholder="Team Members" search="[Function]">
|
||||
<vheader-stub styletype="common" placeholder="Team Members" search="[Function]">
|
||||
<!---->
|
||||
<div class="header-selected-members">
|
||||
<vbutton-stub label="Delete" width="122px" height="48px" fontsize="16px" borderradius="6px" onpress="[Function]" class="button deletion"></vbutton-stub>
|
||||
|
Loading…
Reference in New Issue
Block a user