mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-22 16:29:09 +01:00
cipher listing with action button and pop comps
This commit is contained in:
parent
fa4589c15f
commit
3a9a7d3e64
2
jslib
2
jslib
@ -1 +1 @@
|
||||
Subproject commit a0ca51dda4ebbf710284a0f4e59d47ac8f31c8e7
|
||||
Subproject commit 22f0f97cda0286859ceb889b9c80b9b5bb88affa
|
@ -283,8 +283,8 @@
|
||||
"viewItem": {
|
||||
"message": "View Item"
|
||||
},
|
||||
"launchWebsite": {
|
||||
"message": "Launch Website"
|
||||
"launch": {
|
||||
"message": "Launch"
|
||||
},
|
||||
"website": {
|
||||
"message": "Website"
|
||||
@ -383,8 +383,14 @@
|
||||
"message": "Verification code is required."
|
||||
},
|
||||
"valueCopied": {
|
||||
"message": " copied",
|
||||
"description": "' copied'. This is part of a sentence so be sure to leave the space prefix. For example: 'Password copied'"
|
||||
"message": "$VALUE$ copied",
|
||||
"description": "Value has been copied to the clipboard.",
|
||||
"placeholders": {
|
||||
"value": {
|
||||
"content": "$1",
|
||||
"example": "Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillError": {
|
||||
"message": "Unable to auto-fill the selected item on this page. Copy and paste the information instead."
|
||||
|
@ -15,6 +15,7 @@ import { RegisterComponent } from './accounts/register.component';
|
||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
import { TabsComponent } from './tabs.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
|
||||
@ -29,6 +30,7 @@ const routes: Routes = [
|
||||
{ path: 'register', component: RegisterComponent },
|
||||
{ path: 'hint', component: HintComponent },
|
||||
{ path: 'environment', component: EnvironmentComponent },
|
||||
{ path: 'ciphers', component: CiphersComponent },
|
||||
{
|
||||
path: 'tabs', component: TabsComponent,
|
||||
children: [
|
||||
|
@ -24,6 +24,7 @@ import { RegisterComponent } from './accounts/register.component';
|
||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
import { TabsComponent } from './tabs.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
|
||||
@ -36,6 +37,12 @@ import { StopClickDirective } from 'jslib/angular/directives/stop-click.directiv
|
||||
import { StopPropDirective } from 'jslib/angular/directives/stop-prop.directive';
|
||||
|
||||
import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
||||
import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
||||
|
||||
import { ActionButtonsComponent } from './components/action-buttons.component';
|
||||
import { PopOutComponent } from './components/pop-out.component';
|
||||
|
||||
import { IconComponent } from 'jslib/angular/components/icon.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -52,11 +59,13 @@ import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
||||
ToasterModule,
|
||||
],
|
||||
declarations: [
|
||||
ActionButtonsComponent,
|
||||
ApiActionDirective,
|
||||
AppComponent,
|
||||
AutofocusDirective,
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
CiphersComponent,
|
||||
CurrentTabComponent,
|
||||
EnvironmentComponent,
|
||||
FallbackSrcDirective,
|
||||
@ -64,9 +73,12 @@ import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
||||
HomeComponent,
|
||||
HintComponent,
|
||||
I18nPipe,
|
||||
IconComponent,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
PopOutComponent,
|
||||
RegisterComponent,
|
||||
SearchCiphersPipe,
|
||||
StopClickDirective,
|
||||
StopPropDirective,
|
||||
TabsComponent,
|
||||
|
37
src/popup2/components/action-buttons.component.html
Normal file
37
src/popup2/components/action-buttons.component.html
Normal file
@ -0,0 +1,37 @@
|
||||
<span class="row-btn" (click)="view()" appStopClick appBlurClick title="{{'view' | i18n}}" *ngIf="showView">
|
||||
<i class="fa fa-lg fa-eye"></i>
|
||||
</span>
|
||||
<ng-container *ngIf="cipher.type === cipherType.Login">
|
||||
<span class="row-btn" appStopClick appBlurClick title="{{'launch' | i18n}}" (click)="launch()"
|
||||
*ngIf="!showView" [ngClass]="{disabled: !cipher.login.canLaunch}">
|
||||
<i class="fa fa-lg fa-share-square-o"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appBlurClick title="{{'copyUsername' | i18n}}"
|
||||
(click)="copy(cipher.login.username, 'username', 'Username')"
|
||||
[ngClass]="{disabled: !cipher.login.username}">
|
||||
<i class="fa fa-lg fa-user"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appBlurClick title="{{'copyPassword' | i18n}}"
|
||||
(click)="copy(cipher.login.password, 'password', 'Password')"
|
||||
[ngClass]="{disabled: !cipher.login.password}">
|
||||
<i class="fa fa-lg fa-key"></i>
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="cipher.type === cipherType.Card">
|
||||
<span class="row-btn" appStopClick appBlurClick title="{{'copyNumber' | i18n}}"
|
||||
(click)="copy(cipher.card.number, 'number', 'Card Number')"
|
||||
[ngClass]="{disabled: !cipher.card.number}">
|
||||
<i class="fa fa-lg fa-hashtag"></i>
|
||||
</span>
|
||||
<span class="row-btn" appStopClick appBlurClick title="{{'copySecurityCode' | i18n}}"
|
||||
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')"
|
||||
[ngClass]="{disabled: !cipher.card.code}">
|
||||
<i class="fa fa-lg fa-key"></i>
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="cipher.type === cipherType.SecureNote">
|
||||
<span class="row-btn" appStopClick appBlurClick title="{{'copyNote' | i18n}}"
|
||||
(click)="copy(cipher.notes, 'note', 'Note')" [ngClass]="{disabled: !cipher.notes}">
|
||||
<i class="fa fa-lg fa-clipboard"></i>
|
||||
</span>
|
||||
</ng-container>
|
64
src/popup2/components/action-buttons.component.ts
Normal file
64
src/popup2/components/action-buttons.component.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import * as template from './action-buttons.component.html';
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
|
||||
import { CipherType } from 'jslib/enums/cipherType';
|
||||
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-action-buttons',
|
||||
template: template,
|
||||
})
|
||||
export class ActionButtonsComponent {
|
||||
@Output() onView = new EventEmitter<CipherView>();
|
||||
@Input() cipher: CipherView;
|
||||
@Input() showView: boolean = false;
|
||||
|
||||
cipherType = CipherType;
|
||||
|
||||
constructor(private analytics: Angulartics2, private toasterService: ToasterService,
|
||||
private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
|
||||
private popupUtilsService: PopupUtilsService) { }
|
||||
|
||||
launch() {
|
||||
if (this.cipher.type !== CipherType.Login || !this.cipher.login.canLaunch) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.analytics.eventTrack.next({ action: 'Launched URI From Listing' });
|
||||
BrowserApi.createNewTab(this.cipher.login.uri);
|
||||
if (this.popupUtilsService.inPopup(window)) {
|
||||
BrowserApi.closePopup(window);
|
||||
}
|
||||
}
|
||||
|
||||
copy(value: string, typeI18nKey: string, aType: string) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.analytics.eventTrack.next({ action: 'Copied ' + aType });
|
||||
this.platformUtilsService.copyToClipboard(value);
|
||||
this.toasterService.popAsync('info', null,
|
||||
this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey)));
|
||||
}
|
||||
|
||||
view() {
|
||||
this.onView.emit(this.cipher);
|
||||
}
|
||||
}
|
3
src/popup2/components/pop-out.component.html
Normal file
3
src/popup2/components/pop-out.component.html
Normal file
@ -0,0 +1,3 @@
|
||||
<button (click)="expand()" title="{{'popOutNewWindow' | i18n}}">
|
||||
<i class="fa fa-external-link fa-rotate-270 fa-lg"></i>
|
||||
</button>
|
64
src/popup2/components/pop-out.component.ts
Normal file
64
src/popup2/components/pop-out.component.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import * as template from './pop-out.component.html';
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pop-out',
|
||||
template: template,
|
||||
})
|
||||
export class PopOutComponent {
|
||||
constructor(private analytics: Angulartics2, private platformUtilsService: PlatformUtilsService,
|
||||
private popupUtilsService: PopupUtilsService) { }
|
||||
|
||||
expand() {
|
||||
this.analytics.eventTrack.next({ action: 'Pop Out Window' });
|
||||
|
||||
let href = window.location.href;
|
||||
if (this.platformUtilsService.isEdge()) {
|
||||
const popupIndex = href.indexOf('/popup/');
|
||||
if (popupIndex > -1) {
|
||||
href = href.substring(popupIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if ((typeof chrome !== 'undefined') && chrome.windows && chrome.windows.create) {
|
||||
if (href.indexOf('?uilocation=') > -1) {
|
||||
href = href.replace('uilocation=popup', 'uilocation=popout')
|
||||
.replace('uilocation=tab', 'uilocation=popout')
|
||||
.replace('uilocation=sidebar', 'uilocation=popout');
|
||||
} else {
|
||||
const hrefParts = href.split('#');
|
||||
href = hrefParts[0] + '?uilocation=popout' + (hrefParts.length > 0 ? '#' + hrefParts[1] : '');
|
||||
}
|
||||
|
||||
const bodyRect = document.querySelector('body').getBoundingClientRect();
|
||||
chrome.windows.create({
|
||||
url: href,
|
||||
type: 'popup',
|
||||
width: bodyRect.width + 60,
|
||||
height: bodyRect.height,
|
||||
});
|
||||
|
||||
if (this.popupUtilsService.inPopup(window)) {
|
||||
BrowserApi.closePopup(window);
|
||||
}
|
||||
} else if ((typeof chrome !== 'undefined') && chrome.tabs && chrome.tabs.create) {
|
||||
href = href.replace('uilocation=popup', 'uilocation=tab')
|
||||
.replace('uilocation=popout', 'uilocation=tab')
|
||||
.replace('uilocation=sidebar', 'uilocation=tab');
|
||||
chrome.tabs.create({
|
||||
url: href,
|
||||
});
|
||||
} else if ((typeof safari !== 'undefined')) {
|
||||
// Safari can't open popup in full page tab :(
|
||||
}
|
||||
}
|
||||
}
|
21
src/popup2/services/popup-utils.service.ts
Normal file
21
src/popup2/services/popup-utils.service.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class PopupUtilsService {
|
||||
inSidebar(win: Window): boolean {
|
||||
return win.location.search !== '' && win.location.search.indexOf('uilocation=sidebar') > -1;
|
||||
}
|
||||
|
||||
inTab(win: Window): boolean {
|
||||
return win.location.search !== '' && win.location.search.indexOf('uilocation=tab') > -1;
|
||||
}
|
||||
|
||||
inPopout(win: Window): boolean {
|
||||
return win.location.search !== '' && win.location.search.indexOf('uilocation=popout') > -1;
|
||||
}
|
||||
|
||||
inPopup(win: Window): boolean {
|
||||
return win.location.search === '' || win.location.search.indexOf('uilocation=') === -1 ||
|
||||
win.location.search.indexOf('uilocation=popup') > -1;
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { SettingsService } from 'jslib/abstractions/settings.service';
|
||||
import { StateService as StateServiceAbstraction } from 'jslib/abstractions/state.service';
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
import { SyncService } from 'jslib/abstractions/sync.service';
|
||||
import { TokenService } from 'jslib/abstractions/token.service';
|
||||
@ -33,11 +34,13 @@ import { UserService } from 'jslib/abstractions/user.service';
|
||||
import { UtilsService } from 'jslib/abstractions/utils.service';
|
||||
|
||||
import { AutofillService } from '../../services/abstractions/autofill.service';
|
||||
|
||||
import BrowserMessagingService from '../../services/browserMessaging.service';
|
||||
|
||||
import { AuthService } from 'jslib/services/auth.service';
|
||||
import { ConstantsService } from 'jslib/services/constants.service';
|
||||
import { StateService } from 'jslib/services/state.service';
|
||||
|
||||
import { PopupUtilsService } from './popup-utils.service';
|
||||
|
||||
function getBgService<T>(service: string) {
|
||||
return (): T => {
|
||||
@ -46,6 +49,7 @@ function getBgService<T>(service: string) {
|
||||
};
|
||||
}
|
||||
|
||||
const stateService = new StateService();
|
||||
const messagingService = new BrowserMessagingService(getBgService<PlatformUtilsService>('platformUtilsService')());
|
||||
const authService = new AuthService(getBgService<CryptoService>('cryptoService')(),
|
||||
getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(),
|
||||
@ -53,11 +57,16 @@ const authService = new AuthService(getBgService<CryptoService>('cryptoService')
|
||||
getBgService<I18nService>('i18n2Service')(), getBgService<PlatformUtilsService>('platformUtilsService')(),
|
||||
getBgService<ConstantsService>('constantsService')(), messagingService);
|
||||
|
||||
function initFactory(): Function {
|
||||
function initFactory(i18nService: I18nService, storageService: StorageService): Function {
|
||||
return async () => {
|
||||
if (getBgService<I18nService>('i18n2Service')() != null) {
|
||||
const htmlEl = window.document.documentElement;
|
||||
if (i18nService != null) {
|
||||
authService.init();
|
||||
htmlEl.classList.add('locale_' + i18nService.translationLocale);
|
||||
}
|
||||
|
||||
stateService.save(ConstantsService.disableFaviconKey,
|
||||
await storageService.get<boolean>(ConstantsService.disableFaviconKey));
|
||||
};
|
||||
}
|
||||
|
||||
@ -69,8 +78,10 @@ function initFactory(): Function {
|
||||
providers: [
|
||||
ValidationService,
|
||||
AuthGuardService,
|
||||
PopupUtilsService,
|
||||
{ provide: MessagingService, useValue: messagingService },
|
||||
{ provide: AuthServiceAbstraction, useValue: authService },
|
||||
{ provide: StateServiceAbstraction, useValue: stateService },
|
||||
{ provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] },
|
||||
{ provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] },
|
||||
{ provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] },
|
||||
@ -102,7 +113,7 @@ function initFactory(): Function {
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
deps: [],
|
||||
deps: [I18nService, StorageService],
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
|
50
src/popup2/vault/ciphers.component.html
Normal file
50
src/popup2/vault/ciphers.component.html
Normal file
@ -0,0 +1,50 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/tabs/vault">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
<span>{{'back' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="center">
|
||||
{{searchPlaceholder || ('searchVault' | i18n)}}
|
||||
</div>
|
||||
<div class="right">
|
||||
<button appBlurClick (click)="addCipher()" title="{{'addItem' | i18n}}">
|
||||
<i class="fa fa-plus fa-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<ng-container *ngIf="(ciphers | searchCiphers: searchText) as searchedCiphers">
|
||||
<div class="box list" *ngIf="searchedCiphers.length > 0">
|
||||
<div class="box-header">
|
||||
Some name here
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a *ngFor="let c of searchedCiphers" appStopClick (click)="selectCipher(c)"
|
||||
href="#" title="{{'viewItem' | i18n}}" class="box-content-row box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
<span class="text">
|
||||
{{c.name}}
|
||||
<i class="fa fa-share-alt text-muted" *ngIf="c.organizationId"
|
||||
title="{{'shared' | i18n}}"></i>
|
||||
<i class="fa fa-paperclip text-muted" *ngIf="c.hasAttachments"
|
||||
title="{{'attachments' | i18n}}"></i>
|
||||
</span>
|
||||
<span class="detail">{{c.subTitle}}</span>
|
||||
</div>
|
||||
<app-action-buttons [cipher]="c" class="action-buttons"></app-action-buttons>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!searchedCiphers.length">
|
||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded"></i>
|
||||
<ng-container *ngIf="loaded">
|
||||
<i class="fa fa-frown-o fa-4x"></i>
|
||||
<p>{{'noItemsInList' | i18n}}</p>
|
||||
<button (click)="addCipher()" class="btn block primary link">{{'addItem' | i18n}}</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</content>
|
45
src/popup2/vault/ciphers.component.ts
Normal file
45
src/popup2/vault/ciphers.component.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import * as template from './ciphers.component.html';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
|
||||
import { CiphersComponent as BaseCiphersComponent } from 'jslib/angular/components/ciphers.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-ciphers',
|
||||
template: template,
|
||||
})
|
||||
export class CiphersComponent extends BaseCiphersComponent implements OnInit {
|
||||
constructor(cipherService: CipherService, private route: ActivatedRoute) {
|
||||
super(cipherService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.queryParams.subscribe(async (params) => {
|
||||
if (params.type) {
|
||||
const t = parseInt(params.type, null);
|
||||
await super.load((c) => c.type === t);
|
||||
} else if (params.folderId) {
|
||||
await super.load((c) => c.folderId === params.folderId);
|
||||
} else if (params.collectionId) {
|
||||
await super.load((c) => c.collectionIds.indexOf(params.collectionId) > -1);
|
||||
} else {
|
||||
await super.load();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
selectCipher(cipher: CipherView) {
|
||||
super.selectCipher(cipher);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
|
||||
<app-pop-out></app-pop-out>
|
||||
</div>
|
||||
<div class="center">
|
||||
<span class="title">{{'myVault' | i18n}}</span>
|
||||
|
@ -4,10 +4,13 @@ import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { CipherType } from 'jslib/enums/cipherType';
|
||||
|
||||
import { CollectionView } from 'jslib/models/view/collectionView';
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
import { FolderView } from 'jslib/models/view/folderView';
|
||||
|
||||
import { CollectionService } from 'jslib/abstractions/collection.service';
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
@ -27,15 +30,15 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
||||
typeCounts = new Map<CipherType, number>();
|
||||
|
||||
constructor(collectionService: CollectionService, folderService: FolderService,
|
||||
private cipherService: CipherService) {
|
||||
private cipherService: CipherService, private router: Router) {
|
||||
super(collectionService, folderService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.load();
|
||||
this.loaded = false;
|
||||
super.load();
|
||||
super.loaded = false;
|
||||
await this.loadCiphers();
|
||||
this.loaded = true;
|
||||
super.loaded = true;
|
||||
}
|
||||
|
||||
async loadCiphers() {
|
||||
@ -71,4 +74,19 @@ export class GroupingsComponent extends BaseGroupingsComponent implements OnInit
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
selectType(type: CipherType) {
|
||||
super.selectType(type);
|
||||
this.router.navigate(['/ciphers', { queryParams: { type: type } }]);
|
||||
}
|
||||
|
||||
selectFolder(folder: FolderView) {
|
||||
super.selectFolder(folder);
|
||||
this.router.navigate(['/ciphers', { queryParams: { folderId: folder.id } }]);
|
||||
}
|
||||
|
||||
selectCollection(collection: CollectionView) {
|
||||
super.selectCollection(collection);
|
||||
this.router.navigate(['/ciphers', { queryParams: { collectionId: collection.id } }]);
|
||||
}
|
||||
}
|
||||
|
@ -113,15 +113,16 @@ header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button, a {
|
||||
app-pop-out > button, div > button, div > a {
|
||||
background: $brand-primary;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 0 10px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
@ -131,6 +132,14 @@ header {
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
i + span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
app-pop-out {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -255,4 +255,55 @@
|
||||
font-size: $font-size-small;
|
||||
color: $text-muted;
|
||||
}
|
||||
|
||||
&.list {
|
||||
margin-bottom: 0;
|
||||
|
||||
.box-content {
|
||||
.box-content-row {
|
||||
padding: 3px 10px;
|
||||
color: $text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover, &:focus, &.active {
|
||||
background-color: $list-item-hover;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-left: 5px solid $text-muted;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.text, .detail {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: $font-size-small;
|
||||
color: $gray-light;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
float: left;
|
||||
height: 36px;
|
||||
width: 34px;
|
||||
margin-left: -5px;
|
||||
color: $text-muted;
|
||||
|
||||
img {
|
||||
border-radius: $border-radius;
|
||||
max-height: 20px;
|
||||
max-width: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user