first chrome implementation
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<!-- eslint-disable @angular-eslint/template/elements-content -->
|
||||
<div [id]="idString" class="modal fade" data-bs-backdrop="static" tabindex="-1">
|
||||
<div
|
||||
class="modal-dialog modal-sm modal-dialog-centered modal-dialog-scrollable"
|
||||
>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Please confirm</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
(click)="modal?.hide(); no.emit()"
|
||||
></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span>{{ message }}</span>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" (click)="modal?.hide()">
|
||||
No
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" (click)="onClickYes()">
|
||||
Yes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
:host {
|
||||
.modal {
|
||||
--bs-modal-margin: 32px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ConfirmComponent } from './confirm.component';
|
||||
|
||||
describe('ConfirmComponent', () => {
|
||||
let component: ConfirmComponent;
|
||||
let fixture: ComponentFixture<ConfirmComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ConfirmComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ConfirmComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { AfterViewInit, Component, EventEmitter, Output } from '@angular/core';
|
||||
import * as bootstrap from 'bootstrap';
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'lib-confirm',
|
||||
imports: [],
|
||||
templateUrl: './confirm.component.html',
|
||||
styleUrl: './confirm.component.scss',
|
||||
})
|
||||
export class ConfirmComponent implements AfterViewInit {
|
||||
@Output() yes = new EventEmitter<void>();
|
||||
@Output() no = new EventEmitter<void>();
|
||||
|
||||
message: string | undefined;
|
||||
onYes: ((() => Promise<void>) | (() => void)) | undefined;
|
||||
modal: bootstrap.Modal | undefined;
|
||||
|
||||
readonly idString = crypto.randomUUID();
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const myModalEl = document.getElementById(this.idString);
|
||||
if (!myModalEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.modal = new bootstrap.Modal(myModalEl);
|
||||
}
|
||||
|
||||
onClickYes() {
|
||||
this.modal?.hide();
|
||||
if (typeof this.onYes !== 'undefined') {
|
||||
this.onYes();
|
||||
}
|
||||
}
|
||||
|
||||
show(message: string, onYes: (() => Promise<void>) | (() => void)): void {
|
||||
this.message = message;
|
||||
this.onYes = onYes;
|
||||
this.modal?.show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="icon-button">
|
||||
<i [class]="'bi bi-' + icon"></i>
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
:host {
|
||||
.icon-button {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
//padding: 10px;
|
||||
|
||||
border-radius: 100%;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { IconButtonComponent } from './icon-button.component';
|
||||
|
||||
describe('IconButtonComponent', () => {
|
||||
let component: IconButtonComponent;
|
||||
let fixture: ComponentFixture<IconButtonComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [IconButtonComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(IconButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'lib-icon-button',
|
||||
imports: [],
|
||||
templateUrl: './icon-button.component.html',
|
||||
styleUrl: './icon-button.component.scss',
|
||||
})
|
||||
export class IconButtonComponent {
|
||||
@Input({ required: true }) icon!: string;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<span class="sam-no-select text">
|
||||
{{ text }}
|
||||
</span>
|
||||
|
||||
<div class="sam-flex-grow"></div>
|
||||
|
||||
<div class="buttons sam-flex-row gap-h">
|
||||
<lib-icon-button
|
||||
icon="arrow-right"
|
||||
style="pointer-events: none"
|
||||
></lib-icon-button>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
:host {
|
||||
cursor: pointer;
|
||||
height: 48px;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
padding-right: 8px;
|
||||
background: var(--background-light);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:hover {
|
||||
background: var(--background-light-hover);
|
||||
}
|
||||
|
||||
.text {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavItemComponent } from './nav-item.component';
|
||||
|
||||
describe('NavItemComponent', () => {
|
||||
let component: NavItemComponent;
|
||||
let fixture: ComponentFixture<NavItemComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [NavItemComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(NavItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { IconButtonComponent } from "../icon-button/icon-button.component";
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'lib-nav-item',
|
||||
imports: [IconButtonComponent],
|
||||
templateUrl: './nav-item.component.html',
|
||||
styleUrl: './nav-item.component.scss',
|
||||
})
|
||||
export class NavItemComponent {
|
||||
@Input({ required: true }) text!: string;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<span [style.color]="color">{{ npubString }}</span>
|
||||
|
||||
<lib-icon-button icon="copy" (click)="copyToClipboard()"></lib-icon-button>
|
||||
@@ -0,0 +1,6 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: calc(var(--size) / 2);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PubkeyComponent } from './pubkey.component';
|
||||
|
||||
describe('PubkeyComponent', () => {
|
||||
let component: PubkeyComponent;
|
||||
let fixture: ComponentFixture<PubkeyComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PubkeyComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PubkeyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { NostrHelper } from '@common';
|
||||
import { IconButtonComponent } from "../icon-button/icon-button.component";
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'lib-pubkey',
|
||||
imports: [IconButtonComponent],
|
||||
templateUrl: './pubkey.component.html',
|
||||
styleUrl: './pubkey.component.scss',
|
||||
})
|
||||
export class PubkeyComponent implements OnInit {
|
||||
@Input({ required: true }) value!: string;
|
||||
@Input() first = 9;
|
||||
@Input() last = 5;
|
||||
@Input() color = '#dee2e6bf';
|
||||
|
||||
npub: string | undefined;
|
||||
npubString: string | undefined;
|
||||
|
||||
ngOnInit(): void {
|
||||
const pubkeyObject = NostrHelper.getNostrPubkeyObject(this.value);
|
||||
this.npub = pubkeyObject.npub;
|
||||
this.npubString = NostrHelper.splitKey(
|
||||
pubkeyObject.npub,
|
||||
this.first,
|
||||
this.last
|
||||
);
|
||||
}
|
||||
|
||||
copyToClipboard() {
|
||||
if (!this.npub) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(this.npub);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<span>{{ type }}</span>
|
||||
@@ -0,0 +1,31 @@
|
||||
:host {
|
||||
border-radius: 4px;
|
||||
padding: 0px 4px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
border: 1px solid transparent;
|
||||
min-width: 40px;
|
||||
min-height: 20px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&.read {
|
||||
&:not(.is-selected) {
|
||||
border: 1px solid var(--bs-green);
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background: var(--bs-green);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.read) {
|
||||
&:not(.is-selected) {
|
||||
border: 1px solid var(--bs-primary);
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background: var(--bs-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RelayRwComponent } from './relay-rw.component';
|
||||
|
||||
describe('RelayRwComponent', () => {
|
||||
let component: RelayRwComponent;
|
||||
let fixture: ComponentFixture<RelayRwComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [RelayRwComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RelayRwComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Input,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'lib-relay-rw',
|
||||
imports: [],
|
||||
templateUrl: './relay-rw.component.html',
|
||||
styleUrl: './relay-rw.component.scss',
|
||||
})
|
||||
export class RelayRwComponent {
|
||||
@Input({ required: true }) type!: 'read' | 'write';
|
||||
@Input({ required: true }) model!: boolean;
|
||||
@Output() modelChange = new EventEmitter<boolean>();
|
||||
|
||||
@HostBinding('class.read') get isRead() {
|
||||
return this.type === 'read';
|
||||
}
|
||||
|
||||
@HostBinding('class.is-selected') get isSelected() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
@HostListener('click') onClick() {
|
||||
this.model = !this.model;
|
||||
this.modelChange.emit(this.model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div
|
||||
[id]="idString"
|
||||
class="toast hide"
|
||||
style="width: 100%"
|
||||
role="alert"
|
||||
>
|
||||
<div class="toast-body">{{ message }}</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
:host {
|
||||
position: absolute;
|
||||
align-self: center;
|
||||
//bottom: 76px;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ToastComponent } from './toast.component';
|
||||
|
||||
describe('ToastComponent', () => {
|
||||
let component: ToastComponent;
|
||||
let fixture: ComponentFixture<ToastComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ToastComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ToastComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
37
projects/common/src/lib/components/toast/toast.component.ts
Normal file
37
projects/common/src/lib/components/toast/toast.component.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { AfterViewInit, Component, HostBinding, Input } from '@angular/core';
|
||||
import * as bootstrap from 'bootstrap';
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'lib-toast',
|
||||
imports: [],
|
||||
templateUrl: './toast.component.html',
|
||||
styleUrl: './toast.component.scss',
|
||||
})
|
||||
export class ToastComponent implements AfterViewInit {
|
||||
@Input() message: string | undefined;
|
||||
|
||||
@Input()
|
||||
@HostBinding('style.bottom.px')
|
||||
bottom = 76;
|
||||
|
||||
readonly idString = crypto.randomUUID();
|
||||
|
||||
toast: bootstrap.Toast | undefined;
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const myToastEl = document.getElementById(this.idString);
|
||||
if (!myToastEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.toast = new bootstrap.Toast(myToastEl, { delay: 2000 });
|
||||
}
|
||||
|
||||
show(message?: string) {
|
||||
if (message) {
|
||||
this.message = message;
|
||||
}
|
||||
this.toast?.show();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user