[PM-2804] Migrate Send List to Component Library (#5796)

* Migrate Send list html to use Component Library and TailWind

* Added ComponentLibrary search module to Send

* Updated the No-Items on Send List to the new CL

* Added missing type on button at send.component.html

* Changed send to standalone component on web

* Moved no send icon to the consuming component

* removed unnecessary href on send component html

* Added sort to send list

* Removed SendComponent from loose-components module and added on app module

* Removed unnecessary click handler on send name button

* Created own file for no-send icon

* set icons folder as lowercase

* Corrected no-send.icon import

* Setting name as default sort on send list

* Added extra height to no-send.icon and removed classes from bit-no-items title container

* Removed the size small from options button
This commit is contained in:
aj-rosado 2023-07-31 12:04:33 +01:00 committed by GitHub
parent 49549cc150
commit 7ef2acc11a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 111 additions and 57 deletions

View File

@ -9,6 +9,7 @@ import { AppComponent } from "./app.component";
import { CoreModule } from "./core";
import { OssRoutingModule } from "./oss-routing.module";
import { OssModule } from "./oss.module";
import { SendComponent } from "./tools/send/send.component";
import { WildcardRoutingModule } from "./wildcard-routing.module";
@NgModule({
@ -21,6 +22,7 @@ import { WildcardRoutingModule } from "./wildcard-routing.module";
DragDropModule,
LayoutModule,
OssRoutingModule,
SendComponent,
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
],
declarations: [AppComponent],

View File

@ -95,7 +95,6 @@ import { PasswordGeneratorHistoryComponent } from "../tools/password-generator-h
import { AccessComponent } from "../tools/send/access.component";
import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component";
import { EffluxDatesComponent as SendEffluxDatesComponent } from "../tools/send/efflux-dates.component";
import { SendComponent } from "../tools/send/send.component";
import { ToolsComponent } from "../tools/tools.component";
import { PasswordRepromptComponent } from "../vault/components/password-reprompt.component";
import { PremiumBadgeComponent } from "../vault/components/premium-badge.component";
@ -199,7 +198,6 @@ import { SharedModule } from "./shared.module";
SecurityKeysComponent,
SelectableAvatarComponent,
SendAddEditComponent,
SendComponent,
SendEffluxDatesComponent,
SetPasswordComponent,
SettingsComponent,
@ -304,7 +302,6 @@ import { SharedModule } from "./shared.module";
SecurityKeysComponent,
SelectableAvatarComponent,
SendAddEditComponent,
SendComponent,
SendEffluxDatesComponent,
SetPasswordComponent,
SettingsComponent,

View File

@ -0,0 +1,13 @@
import { svgIcon } from "@bitwarden/components";
export const NoSend = svgIcon`
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="125" fill="none">
<path class="tw-stroke-secondary-500" stroke-width="3" d="M13.425 11.994H5.99a4.311 4.311 0 0 0-4.311 4.312v62.09a4.311 4.311 0 0 0 4.311 4.311h40.09"/>
<path class="tw-stroke-secondary-500" stroke-width="3" d="M66.27 75.142h-49.9a3.234 3.234 0 0 1-3.233-3.234V9.818a3.234 3.234 0 0 1 3.234-3.233h35.764a3.233 3.233 0 0 1 2.293.953l14.134 14.216c.602.605.94 1.425.94 2.28v47.874a3.233 3.233 0 0 1-3.233 3.234Z"/>
<path class="tw-stroke-secondary-500" stroke-width="2" d="M47.021 35.586c0-3.818-2.728-6.915-6.095-6.915-3.367 0-6.096 3.097-6.096 6.915"/>
<path class="tw-stroke-secondary-500 tw-fill-secondary-100" stroke-width="2" d="M47.38 35.335H34.058a3.593 3.593 0 0 0-3.593 3.592v9.817a3.593 3.593 0 0 0 3.593 3.593H47.38a3.593 3.593 0 0 0 3.593-3.593v-9.817a3.593 3.593 0 0 0-3.593-3.592Z"/>
<path class="tw-stroke-secondary-500" stroke-linecap="round" stroke-width="2" d="M40.72 44.34v2.618"/>
<path class="tw-stroke-secondary-500" stroke-linecap="round" stroke-width="4" d="M40.72 42.7v-.373"/>
<path class="tw-stroke-secondary-500 tw-fill-secondary-100" stroke-width="3" d="M89.326 64.022s1.673-.73 2.252.572c.512 1.138-.822 2.033-.822 2.033L56.757 88.133a3.886 3.886 0 0 0-1.583 2.188l-4.732 16.705a2.665 2.665 0 0 0 .059 1.611 2.596 2.596 0 0 0 1.891 1.663c.331.07.673.071 1.004.004.402-.077.78-.25 1.102-.503l10.11-7.88a3.138 3.138 0 0 1 1.92-.663 3.08 3.08 0 0 1 1.905.662l13.926 10.948a2.556 2.556 0 0 0 3.162 0 2.71 2.71 0 0 0 .727-.879l31.777-61.762c.231-.448.33-.952.284-1.455a2.606 2.606 0 0 0-1.721-2.226 2.499 2.499 0 0 0-1.457-.06l-81.18 20.418c-.465.12-.888.364-1.223.708a2.672 2.672 0 0 0-.632 2.676c.146.46.417.865.78 1.174L46.2 83.1a4.463 4.463 0 0 0 2.565 1.572 4.489 4.489 0 0 0 2.984-.413l37.578-20.237Z"/>
</svg>
`;

View File

@ -1,24 +1,22 @@
<div class="container page-content">
<app-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
<span>{{ "sendDisabledWarning" | i18n }}</span>
</app-callout>
<div class="row">
<div class="col-3 groupings">
<bit-callout type="warning" title="{{ 'sendDisabled' | i18n }}" *ngIf="disableSend">
{{ "sendDisabledWarning" | i18n }}
</bit-callout>
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
<div class="groupings tw-col-span-3">
<div class="card vault-filters">
<div class="card-header d-flex">
{{ "filters" | i18n }}
</div>
<div class="card-body">
<input
type="search"
placeholder="{{ searchPlaceholder || ('searchSends' | i18n) }}"
id="search"
class="form-control"
[(ngModel)]="searchText"
(input)="searchTextChanged()"
autocomplete="off"
appAutofocus
/>
<div class="tw-mb-4">
<bit-search
[(ngModel)]="searchText"
[placeholder]="'searchSends' | i18n"
(input)="searchTextChanged()"
appAutofocus
/>
</div>
<div class="filter">
<ul class="filter-options">
<li class="filter-option" [ngClass]="{ active: selectedAll }">
@ -64,9 +62,9 @@
</div>
</div>
</div>
<div class="col-9">
<div class="page-header d-flex">
<h1>
<div class="tw-col-span-9">
<div class="tw-flex">
<h1 bitTypography="h1">
{{ "send" | i18n }}
<small #actionSpinner [appApiAction]="actionPromise">
<ng-container *ngIf="$any(actionSpinner).loading">
@ -79,29 +77,38 @@
</ng-container>
</small>
</h1>
<div class="ml-auto d-flex">
<div class="tw-ml-auto">
<button
type="button"
class="btn btn-outline-primary btn-sm"
bitButton
buttonType="primary"
(click)="addSend()"
[disabled]="disableSend"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>{{ "createSend" | i18n }}
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "createSend" | i18n }}
</button>
</div>
</div>
<!--Listing Table-->
<table class="table table-hover table-list" *ngIf="filteredSends && filteredSends.length">
<tbody>
<tr *ngFor="let s of filteredSends">
<td class="table-list-icon">
<div class="icon" aria-hidden="true">
<bit-table [dataSource]="dataSource" *ngIf="filteredSends && filteredSends.length">
<ng-container header>
<tr>
<th bitCell bitSortable="name" default>{{ "name" | i18n }}</th>
<th bitCell bitSortable="deletionDate">{{ "deletionDate" | i18n }}</th>
<th bitCell>{{ "options" | i18n }}</th>
</tr>
</ng-container>
<ng-template body let-rows$>
<tr bitRow *ngFor="let s of rows$ | async">
<td bitCell (click)="editSend(s)" class="tw-cursor-pointer">
<span class="tw-mr-2" aria-hidden="true">
<i class="bwi bwi-fw bwi-lg bwi-file" *ngIf="s.type == sendType.File"></i>
<i class="bwi bwi-fw bwi-lg bwi-file-text" *ngIf="s.type == sendType.Text"></i>
</div>
</td>
<td class="reduced-lh wrap">
<a href="#" appStopClick appStopProp (click)="editSend(s)">{{ s.name }}</a>
</span>
<button type="button" bitLink>
{{ s.name }}
</button>
<ng-container *ngIf="s.disabled">
<i
class="bwi bwi-exclamation-triangle"
@ -147,19 +154,19 @@
></i>
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
</ng-container>
<br />
<small appStopProp>{{ s.deletionDate | date : "medium" }}</small>
</td>
<td class="table-list-options">
<td bitCell class="tw-text-muted" (click)="editSend(s)" class="tw-cursor-pointer">
<small bitTypography="body2" appStopProp>{{
s.deletionDate | date : "medium"
}}</small>
</td>
<td bitCell class="tw-w-0 tw-text-right">
<button
type="button"
[bitMenuTriggerFor]="sendOptions"
class="tw-border-none tw-bg-transparent tw-text-main"
type="button"
bitIconButton="bwi-ellipsis-v"
appA11yTitle="{{ 'options' | i18n }}"
>
<i class="bwi bwi-ellipsis-v bwi-lg" aria-hidden="true"></i>
</button>
></button>
<bit-menu #sendOptions>
<button type="button" bitMenuItem (click)="copy(s)">
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
@ -183,8 +190,8 @@
</bit-menu>
</td>
</tr>
</tbody>
</table>
</ng-template>
</bit-table>
<div class="no-items" *ngIf="filteredSends && !filteredSends.length">
<ng-container *ngIf="!loaded">
<i
@ -195,16 +202,20 @@
<span class="sr-only">{{ "loading" | i18n }}</span>
</ng-container>
<ng-container *ngIf="loaded">
<bit-icon [icon]="noItemIcon" aria-hidden="true"></bit-icon>
<p>{{ "noSendsInList" | i18n }}</p>
<button
type="button"
(click)="addSend()"
class="btn btn-outline-primary"
[disabled]="disableSend"
>
<i class="bwi bwi-plus bwi-fw"></i>{{ "createSend" | i18n }}
</button>
<bit-no-items [icon]="noItemIcon" class="tw-text-main">
<ng-container slot="title">{{ "sendsNoItemsTitle" | i18n }}</ng-container>
<ng-container slot="description">{{ "sendsNoItemsMessage" | i18n }}</ng-container>
<button
slot="button"
type="button"
bitButton
buttonType="secondary"
(click)="addSend()"
>
<i class="bwi bwi-plus" aria-hidden="true"></i>
{{ "createSend" | i18n }}
</button>
</bit-no-items>
</ng-container>
</div>
</div>

View File

@ -13,20 +13,36 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { Icons } from "@bitwarden/components";
import { NoItemsModule, SearchModule, TableDataSource } from "@bitwarden/components";
import { SharedModule } from "../../shared";
import { AddEditComponent } from "./add-edit.component";
import { NoSend } from "./icons/no-send.icon";
const BroadcasterSubscriptionId = "SendComponent";
@Component({
selector: "app-send",
standalone: true,
imports: [SharedModule, SearchModule, NoItemsModule],
templateUrl: "send.component.html",
})
export class SendComponent extends BaseSendComponent {
@ViewChild("sendAddEdit", { read: ViewContainerRef, static: true })
sendAddEditModalRef: ViewContainerRef;
noItemIcon = Icons.Search;
noItemIcon = NoSend;
override set filteredSends(filteredSends: SendView[]) {
super.filteredSends = filteredSends;
this.dataSource.data = filteredSends;
}
override get filteredSends() {
return super.filteredSends;
}
protected dataSource = new TableDataSource<SendView>();
constructor(
sendService: SendService,

View File

@ -6955,6 +6955,14 @@
"selectedRegionFlag": {
"message": "Selected region flag"
},
"sendsNoItemsTitle": {
"message": "No active Sends",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendsNoItemsMessage": {
"message": "Use Send to securely share encrypted information with anyone.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"inviteUsers": {
"message": "Invite Users"
},

View File

@ -25,11 +25,9 @@ export class SendComponent implements OnInit, OnDestroy {
expired = false;
type: SendType = null;
sends: SendView[] = [];
filteredSends: SendView[] = [];
searchText: string;
selectedType: SendType;
selectedAll: boolean;
searchPlaceholder: string;
filter: (cipher: SendView) => boolean;
searchPending = false;
hasSearched = false; // search() function called - returns true if text qualifies for search
@ -41,6 +39,15 @@ export class SendComponent implements OnInit, OnDestroy {
private searchTimeout: any;
private destroy$ = new Subject<void>();
private _filteredSends: SendView[];
get filteredSends(): SendView[] {
return this._filteredSends;
}
set filteredSends(filteredSends: SendView[]) {
this._filteredSends = filteredSends;
}
constructor(
protected sendService: SendService,