Added light & dark theme. Basic theme applied on dialogs/modals. Attempt to apply uniformity to the website.

This commit is contained in:
Tom 2025-03-31 16:59:39 +00:00
parent ea19375ad2
commit d44ec50a6a
46 changed files with 445 additions and 232 deletions

View File

@ -91,7 +91,10 @@
"buildTarget": "hermes-web-angular:build:development"
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"options": {
"allowedHosts": ["*"]
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"

View File

@ -30,7 +30,6 @@
"rxjs-websockets": "^9.0.0",
"tslib": "^2.3.0",
"uuidv4": "^6.2.13",
"vite": "^6.2.3",
"zone.js": "~0.15.0"
},
"devDependencies": {

View File

@ -1,4 +1,4 @@
<body>
<content>
<mat-card>
<mat-card-header>
<mat-card-title-group>
@ -108,19 +108,26 @@
}
</mat-card-content>
<mat-card-actions class="actions"
align="end">
<mat-card-actions class="actions">
@if (!isNew) {
<button mat-raised-button
class="delete"
(click)="deleteAction(action)">Delete</button>
<button mat-button
class="danger"
(click)="deleteAction(action)">
<mat-icon>delete</mat-icon>Delete
</button>
}
<button mat-raised-button
<button mat-button
class="neutral"
disabled="{{waitForResponse}}"
(click)="dialogRef.close()">Cancel</button>
<button mat-raised-button
(click)="dialogRef.close()">
<mat-icon>cancel</mat-icon>Cancel
</button>
<button mat-button
class="confirm"
disabled="{{!formsDirty || !formsValidity || waitForResponse}}"
(click)="save()">Save</button>
(click)="save()">
<mat-icon>save</mat-icon>Save
</button>
</mat-card-actions>
@if (responseError) {
@ -129,4 +136,4 @@
</mat-card-footer>
}
</mat-card>
</body>
</content>

View File

@ -9,12 +9,6 @@
margin: 0;
}
.actions {
display: flex;
flex-direction: row;
justify-content: center;
}
.delete {
background-color: #ea5151;
color: #ba1a1a;

View File

@ -9,17 +9,19 @@ import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { createItemExistsInArrayValidator } from '../../shared/validators/item-exists-in-array';
import { HermesClientService } from '../../hermes-client.service';
import { MatIconModule } from '@angular/material/icon';
@Component({
selector: 'action-item-edit',
imports: [
ReactiveFormsModule,
MatButtonModule,
MatCardModule,
MatDialogModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatSelectModule
MatSelectModule,
ReactiveFormsModule,
],
templateUrl: './action-item-edit.component.html',
styleUrl: './action-item-edit.component.scss'

View File

@ -1,3 +1,5 @@
@use '@angular/material' as mat;
main {
display: grid;
grid-template-columns: repeat(1, 1fr);
@ -15,7 +17,7 @@ main {
border: 1px solid grey;
padding: 1em;
cursor: pointer;
background-color: white;
background-color: transparent;
& span {
display: block;
@ -27,7 +29,6 @@ main {
& .subtitle {
font-size: smaller;
color: lightgrey;
}
}
}

View File

@ -7,19 +7,28 @@ import { MatIconModule } from '@angular/material/icon';
import { MatDialog } from '@angular/material/dialog';
import { ActionItemEditComponent } from '../action-item-edit/action-item-edit.component';
import { HermesClientService } from '../../hermes-client.service';
import { MatSelectModule } from '@angular/material/select';
@Component({
selector: 'action-list',
standalone: true,
imports: [MatButtonModule, MatFormFieldModule, MatIconModule, MatListModule],
imports: [
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatListModule,
MatSelectModule,
],
templateUrl: './action-list.component.html',
styleUrl: './action-list.component.scss'
})
export class ActionListComponent {
@Input() actions: RedeemableAction[] = []
@Output() actionsChange = new EventEmitter<RedeemableAction>();
readonly dialog = inject(MatDialog);
readonly client = inject(HermesClientService);
opened = false;
create(): void {

View File

@ -1,4 +1,4 @@
<body>
<content>
<h3>Redeemable Actions</h3>
<section>
@ -31,4 +31,4 @@
<action-list class="center"
[actions]="actions"
(actionsChange)="items.push($event)" />
</body>
</content>

View File

@ -1,4 +1,4 @@
import { Component, inject, OnInit } from '@angular/core';
import { Component, Inject, inject, OnInit } from '@angular/core';
import { ActionListComponent } from "../action-list/action-list.component";
import { MatSelectModule } from '@angular/material/select';
import { MatFormFieldModule } from '@angular/material/form-field';
@ -9,6 +9,8 @@ import RedeemableAction from '../../shared/models/redeemable-action';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import RedeemableActionService from '../../shared/services/redeemable-action.service';
import { MatButtonModule } from '@angular/material/button';
import { DOCUMENT } from '@angular/common';
interface IActionFilter {
name: string
@ -17,14 +19,15 @@ interface IActionFilter {
@Component({
selector: 'actions',
standalone: true,
imports: [
ActionListComponent,
ReactiveFormsModule,
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatSelectModule
MatSelectModule,
ReactiveFormsModule,
ReactiveFormsModule,
],
templateUrl: './actions.component.html',
styleUrl: './actions.component.scss'
@ -43,11 +46,15 @@ export class ActionsComponent implements OnInit {
private readonly client = inject(HermesClientService);
private readonly redeemableActionService = inject(RedeemableActionService);
private readonly route = inject(ActivatedRoute);
filter = this.filters[0];
searchControl = new FormControl('');
search = '';
items: RedeemableAction[] = [];
constructor(@Inject(DOCUMENT) private document: Document) { }
ngOnInit(): void {
this.route.data.subscribe(data => {
if (!data['redeemableActions'])

View File

@ -1,5 +1,5 @@
import { isPlatformBrowser } from '@angular/common';
import { Component, OnInit, Inject, PLATFORM_ID, NgZone, OnDestroy, inject } from '@angular/core';
import { Component, OnInit, Inject, PLATFORM_ID, NgZone, OnDestroy, inject, HostBinding } from '@angular/core';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { HermesClientService } from './hermes-client.service';
import { AuthUserGuard } from './shared/auth/auth.user.guard'
@ -10,23 +10,42 @@ import { ApiAuthenticationService } from './shared/services/api/api-authenticati
import { AuthModule } from './auth/auth.module';
import { ApiKeyService } from './shared/services/api/api-key.service';
import ApiKey from './shared/models/api-key';
import { ThemeService } from './shared/services/theme.service';
import { OverlayContainer } from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, AuthModule, NavigationComponent],
imports: [
RouterOutlet,
AuthModule,
NavigationComponent
],
providers: [AuthUserGuard],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent implements OnInit, OnDestroy {
private readonly keyService = inject(ApiKeyService);
private readonly overlayContainer = inject(OverlayContainer);
private readonly themeService = inject(ThemeService);
private isBrowser: boolean;
private ngZone: NgZone;
private subscriptions: Subscription[];
@HostBinding('class.dark-theme')
get isDarkTheme() {
return this.themeService.isDarkTheme();
}
@HostBinding('class.light-theme')
get isLightTheme() {
return this.themeService.isLightTheme();
}
constructor(private auth: ApiAuthenticationService, private client: HermesClientService, private events: EventService, private router: Router, ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: Object) {
this.ngZone = ngZone;
this.isBrowser = isPlatformBrowser(this.platformId);
@ -80,6 +99,8 @@ export class AppComponent implements OnInit, OnDestroy {
});
}));
this.overlayContainer.getContainerElement().classList.add(this.themeService.theme + '-theme');
this.ngZone.runOutsideAngular(() => setInterval(() => this.client.heartbeat(), 15000));
}

View File

@ -17,6 +17,6 @@ export const appConfig: ApplicationConfig = {
}])
),
provideOAuthClient(),
provideClientHydration(), provideAnimationsAsync()
provideClientHydration(), provideAnimationsAsync(), provideAnimationsAsync()
]
};

13
src/app/app.module.ts Normal file
View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatDialogModule } from '@angular/material/dialog';
import { TtsFiltersModule } from './tts-filters/tts-filters.module';
@NgModule({
declarations: [],
imports: [
CommonModule,
]
})
export class AppModule { }

View File

@ -18,6 +18,7 @@
.mat-mdc-card-content {
align-self: center;
justify-content: space-between;
}
a {

View File

@ -16,7 +16,7 @@
</mat-select>
</mat-form-field>
</mat-card-content>
<mat-card-actions align="end">
<mat-card-actions>
<button mat-raised-button
(click)="login()">Log In</button>
</mat-card-actions>

View File

@ -8,8 +8,7 @@
<mat-card-content>
<impersonation />
</mat-card-content>
<mat-card-actions class="actions"
align="end">
<mat-card-actions class="actions">
<div>
@if (isTTSLoggedIn) {
<button mat-raised-button

View File

@ -41,13 +41,12 @@
</mat-form-field>
</mat-card-content>
<mat-card-actions class="actions"
align="end">
<button mat-raised-button
class="warning"
<mat-card-actions class="actions">
<button mat-button
class="neutral"
disabled="{{waitForResponse}}"
(click)="dialogRef.close()">Cancel</button>
<button mat-raised-button
<button mat-button
class="confirm"
disabled="{{!form.dirty || form.invalid || waitForResponse}}"
(click)="submit()">Add</button>

View File

@ -1,3 +1,5 @@
<h3>Connections</h3>
<content>
<h3>Connections</h3>
<connection-list [connections]="connections"/>
<connection-list [connections]="connections" />
</content>

View File

@ -38,13 +38,15 @@
}
</mat-form-field>
</mat-card-content>
<mat-card-actions align="end">
<mat-card-actions>
<button mat-button
class="neutral"
[disabled]="waitForResponse"
(click)="cancel()">
<mat-icon>cancel</mat-icon>Cancel
</button>
<button mat-button
class="confirm"
[disabled]="waitForResponse || formGroup.invalid"
(click)="add()">
<mat-icon>add</mat-icon>Add

View File

@ -1,8 +1,8 @@
article {
background-color: #f0f0f0;
display: flex;
flex-direction: row;
justify-content: space-between;
border: grey solid 1px;
border-radius: 15px;
padding: 0.5em 1em;

View File

@ -19,13 +19,12 @@
</mat-form-field>
</mat-card-content>
<mat-card-actions class="actions"
align="end">
<button mat-raised-button
class="warning"
<mat-card-actions class="actions">
<button mat-button
class="neutral"
disabled="{{waitForResponse}}"
(click)="dialogRef.close()">Cancel</button>
<button mat-raised-button
<button mat-button
class="confirm"
disabled="{{!form.dirty || form.invalid || waitForResponse}}"
(click)="submit()">Add</button>

View File

@ -1,6 +1,6 @@
<nav>
<user-card class="card" />
<ul>
<ul class="">
@if (!isLoggedIn()) {
<li>
<a routerLink="/login"
@ -11,7 +11,8 @@
}
@if (isLoggedIn() && !isTTSLoggedIn()) {
<li>
<a routerLink="/tts-login"
<a mat-raised-button
routerLink="/tts-login"
routerLinkActive="active">
TTS Login
</a>
@ -19,48 +20,56 @@
}
@if (isLoggedIn() && isTTSLoggedIn()) {
<li>
<a routerLink="/policies"
<a mat-raised-button
routerLink="/policies"
routerLinkActive="active">
Policies
</a>
</li>
<li>
<a routerLink="/filters"
<a mat-raised-button
routerLink="/filters"
routerLinkActive="active">
Filters
</a>
</li>
<li>
<a routerLink="/actions"
<a mat-raised-button
routerLink="/actions"
routerLinkActive="active">
Actions
</a>
</li>
<li>
<a routerLink="/redemptions"
<a mat-raised-button
routerLink="/redemptions"
routerLinkActive="active">
Redemptions
</a>
</li>
<li>
<a routerLink="/groups"
<a mat-raised-button
routerLink="/groups"
routerLinkActive="active">
Groups
</a>
</li>
<li>
<a routerLink="/connections"
<a mat-raised-button
routerLink="/connections"
routerLinkActive="active">
Connections
</a>
</li>
}
@if (isLoggedIn()) {
<li>
<a routerLink="/keys"
<a mat-raised-button
routerLink="/keys"
routerLinkActive="active">
API Keys
</a>
</li>
}
</ul>
</nav>

View File

@ -1,8 +1,4 @@
$primary_background_color: #EEEEEE;
$primary_font_color: #111111;
$secondary_background_color: #DDDDDD;
$secondary_font_color: #333333;
@use "@angular/material" as mat;
ul {
padding: 0;
@ -18,20 +14,17 @@ li {
}
a {
background-color: transparent;
padding: 1em;
border: 0;
margin: 0;
font-size: large;
text-decoration: none;
color: $primary_font_color;
border-radius: 10px;
}
a:hover {
background-color: #FAFAFA;
}
a.active {
background-color: #F5F5F5;
@include mat.button-overrides(
(
protected-container-height: 1
)
);
}

View File

@ -5,11 +5,18 @@ import { ApiAuthenticationService } from '../shared/services/api/api-authenticat
import { MatCardModule } from '@angular/material/card';
import { AuthModule } from '../auth/auth.module';
import { UserCardComponent } from "../auth/user-card/user-card.component";
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'navigation',
standalone: true,
imports: [RouterModule, MatCardModule, AuthModule, UserCardComponent],
imports: [
AuthModule,
MatButtonModule,
MatCardModule,
RouterModule,
UserCardComponent,
],
templateUrl: './navigation.component.html',
styleUrl: './navigation.component.scss'
})

View File

@ -23,13 +23,19 @@
</mat-card-content>
<mat-card-actions class="actions">
<button mat-raised-button
<button mat-button
class="neutral"
disabled="{{waitForResponse}}"
(click)="dialogRef.close()">Cancel</button>
(click)="dialogRef.close()">
<mat-icon>cancel</mat-icon>Cancel
</button>
<button mat-raised-button
<button mat-button
class="confirm"
disabled="{{!pathControl.dirty || pathControl.invalid || waitForResponse}}"
(click)="submit()">{{data.isNew ? "Add" : "Save"}}</button>
(click)="submit()">
<mat-icon>{{data.isNew ? "add" : "save"}}</mat-icon>{{data.isNew ? "Add" : "Save"}}
</button>
</mat-card-actions>
@if (responseError) {

View File

@ -52,22 +52,17 @@
}
</mat-form-field>
</mat-card-content>
<mat-card-actions align="end">
<mat-card-actions>
<button mat-button
class="neutral"
(click)="dialogRef.close()">
<mat-icon>cancel</mat-icon>Cancel
</button>
@if (isNew) {
<button mat-button
class="confirm"
(click)="save()">
<mat-icon>add</mat-icon>Add
<mat-icon>{{(isNew ? 'add' : 'save')}}</mat-icon>{{(isNew ? 'Add' : 'Save')}}
</button>
} @else {
<button mat-button
(click)="save()">
<mat-icon>save</mat-icon>Save
</button>
}
</mat-card-actions>
@if (responseError) {

View File

@ -2,7 +2,7 @@ import { AfterViewInit, Component, inject, OnInit, ViewChild } from '@angular/co
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
@ -18,6 +18,7 @@ import { PolicyDropdownComponent } from '../policy-dropdown/policy-dropdown.comp
GroupDropdownComponent,
MatButtonModule,
MatCardModule,
MatDialogModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,

View File

@ -31,23 +31,25 @@
}
}
</mat-form-field>
<div class="buttons">
<button mat-icon-button
class="save"
[disabled]="waitForResponse || formGroups.invalid"
(click)="save()">
<mat-icon>save</mat-icon>
</button>
@if (redemption.id) {
<button mat-icon-button
class="delete"
[disabled]="waitForResponse"
(click)="delete()">
<mat-icon>delete</mat-icon>
</button>
}
</div>
</mat-card-content>
<mat-card-actions>
@if (redemption.id) {
<button mat-button
class="danger"
[disabled]="waitForResponse"
(click)="delete()">
<mat-icon>delete</mat-icon>Delete
</button>
}
<button mat-button
class="confirm"
[disabled]="waitForResponse || formGroups.invalid"
(click)="save()">
<mat-icon>save</mat-icon>Save
</button>
</mat-card-actions>
@if (responseError) {
<mat-card-footer>
<small class="error below">{{responseError}}</small>

View File

@ -1,46 +1,4 @@
.mat-mdc-card {
margin: 0.5em;
}
.mat-mdc-card-content {
display: flex;
flex-direction: row;
column-gap: 1em;
padding-bottom: 0;
}
.buttons {
margin: -0.75em;
}
button,
.mdc-button {
display: block;
}
@media (max-width:1000px) {
.mat-mdc-card-content {
flex-direction: column;
margin-bottom: 0.5em;
display: flex;
justify-content: center;
align-items: center;
margin-top: 1em;
}
.buttons {
margin: 0;
}
button~button {
margin-left: 2em;
}
button {
display: inline;
}
.delete {
background-color: rgb(255, 152, 152);
}
}

View File

@ -29,8 +29,7 @@
<th mat-header-cell
*matHeaderCellDef>Twitch Redemption Name</th>
<td mat-cell
*matCellDef="let redemption">{{getTwitchRedemptionNameById(redemption.redemption_id) || 'Unknown
Twitch Redemption'}}</td>
*matCellDef="let redemption">{{getTwitchRedemptionNameById(redemption.redemption_id) || 'Unknown Twitch Redemption'}}</td>
</ng-container>
<ng-container matColumnDef="action-name">

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ThemeService } from './theme.service';
describe('ThemeService', () => {
let service: ThemeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ThemeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,32 @@
import { Injectable, signal } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ThemeService {
private _current = signal<'light' | 'dark'>('dark');
get theme() {
return this._current();
}
set theme(value: 'light' | 'dark') {
this._current.set(value);
}
isDarkTheme() {
return this.theme == 'dark';
}
isLightTheme() {
return this.theme == 'light';
}
toggle() {
if (this.theme == 'light') {
this.theme = 'dark';
} else {
this.theme = 'light';
}
}
}

View File

@ -0,0 +1,6 @@
<button mat-icon-button
class="refresh"
aria-label="Change theme"
(click)="toggle()">
<mat-icon>refresh</mat-icon>
</button>

View File

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ThemeComponent } from './theme.component';
describe('ThemeComponent', () => {
let component: ThemeComponent;
let fixture: ComponentFixture<ThemeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ThemeComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ThemeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,35 @@
import { Component, HostBinding, inject } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { ThemeService } from '../shared/services/theme.service';
@Component({
selector: 'theme',
imports: [
MatButtonModule,
MatIconModule,
],
templateUrl: './theme.component.html',
styleUrl: './theme.component.scss'
})
export class ThemeComponent {
private readonly _themeService = inject(ThemeService);
@HostBinding('class.dark-theme')
get isDarkMode() {
return this._themeService.isDarkTheme();
}
@HostBinding('class.light-theme')
get isLightMode() {
return this._themeService.isLightTheme();
}
set(theme: 'light' | 'dark') {
this._themeService.theme = theme;
}
toggle() {
this._themeService.toggle();
}
}

View File

@ -1,59 +1,66 @@
<mat-card>
<mat-card-header>
<mat-card-title>TTS Filter</mat-card-title>
</mat-card-header>
<mat-card-content>
<h3 mat-dialog-title>TTS Filter</h3>
<mat-dialog-content>
<form [formGroup]="forms">
<div>
<mat-form-field>
<mat-label>Search</mat-label>
<input matInput
cdkFocusInitial
type="text"
formControlName="search" />
@if (forms.get('search')?.invalid && (forms.get('search')?.dirty || forms.get('search')?.touched)) {
<small class="error">Search is required.</small>
}
</mat-form-field>
</div>
<div>
<mat-form-field>
<mat-label>Replace</mat-label>
<input matInput
formControlName="replace" />
</mat-form-field>
</div>
<div>
<mat-form-field>
<mat-label>Regex Options</mat-label>
<mat-select multiple
[formControl]="flagControl"
[compareWith]="compare"
(selectionChange)="onSelectionChange($event)">
<mat-select-trigger>
{{optionsSelected[0] || ''}}
@if ((flagControl.value?.length || 0) > 1) {
<small class="additional-selection">
(+{{flagControl.value!.length - 1}})
</small>
}
</mat-select-trigger>
@for (option of regexOptions; track option) {
<mat-option [value]="option">{{option.name}}</mat-option>
<form [formGroup]="forms">
<div>
<mat-form-field>
<mat-label>Search</mat-label>
<input matInput
cdkFocusInitial
type="text"
formControlName="search" />
@if (forms.get('search')?.invalid && (forms.get('search')?.dirty || forms.get('search')?.touched)) {
<small class="error">Search is required.</small>
}
</mat-form-field>
</div>
<div>
<mat-form-field>
<mat-label>Replace</mat-label>
<input matInput
formControlName="replace" />
</mat-form-field>
</div>
<div>
<mat-form-field>
<mat-label>Regex Options</mat-label>
<mat-select multiple
[formControl]="flagControl"
[compareWith]="compare"
(selectionChange)="onSelectionChange($event)">
<mat-select-trigger>
{{optionsSelected[0] || ''}}
@if ((flagControl.value?.length || 0) > 1) {
<small class="additional-selection">
(+{{flagControl.value!.length - 1}})
</small>
}
</mat-select>
</mat-form-field>
</div>
@if (responseError) {
<small class="error">{{responseError}}</small>
}
</form>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button
(click)="onCancelClick()">Cancel</button>
<button mat-button
(click)="onSaveClick()"
[disabled]="!forms.dirty || forms.invalid || waitForResponse">Save</button>
</mat-dialog-actions>
</mat-select-trigger>
@for (option of regexOptions; track option) {
<mat-option [value]="option">{{option.name}}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
@if (responseError) {
<small class="error">{{responseError}}</small>
}
</form>
</mat-card-content>
<mat-card-actions>
<button mat-button
class="neutral"
(click)="onCancelClick()"
[disabled]="waitForResponse">
<mat-icon>cancel</mat-icon> Cancel</button>
<button mat-button
class="confirm"
(click)="onSaveClick()"
[disabled]="!forms.dirty || forms.invalid || waitForResponse">
<mat-icon>save</mat-icon>Save</button>
</mat-card-actions>
</mat-card>

View File

@ -1,5 +1,5 @@
import { Component, inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogActions, MatDialogTitle, MatDialogContent } from '@angular/material/dialog';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { Filter } from '../../shared/models/filter';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
@ -8,6 +8,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
import { HermesClientService } from '../../hermes-client.service';
import { MatIconModule } from '@angular/material/icon';
@Component({
selector: 'tts-filter-item-edit',
@ -16,10 +17,9 @@ import { HermesClientService } from '../../hermes-client.service';
FormsModule,
MatButtonModule,
MatCardModule,
MatDialogActions,
MatDialogContent,
MatDialogTitle,
MatDialogModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatSelectModule,
ReactiveFormsModule,

View File

@ -22,7 +22,6 @@ ul {
}
>button {
background: #dddddd;
border-radius: 50%;
:hover {

View File

@ -1,7 +1,3 @@
div {
background-color: #fafafa;
}
ul.data {
margin: 0;
padding: 0;
@ -22,10 +18,6 @@ ul.data {
>li:last-child {
border-bottom: 0 solid #aaaaaa;
}
>li:nth-child(2n) {
background-color: #f5f5f5;
}
}
ul.header {
@ -33,7 +25,6 @@ ul.header {
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
background-color: #fafafa;
li {
border-bottom: 0 solid #aaaaaa;
@ -41,7 +32,6 @@ ul.header {
list-style-type: none;
white-space: pre;
text-align: justify;
background-color: #fafafa;
}
li:nth-child(1),

View File

@ -1,5 +1,4 @@
main {
background-color: #fafafa;
display: flex;
flex-direction: column;
height: 100vh;

View File

@ -1,12 +1,17 @@
import { NgModule } from '@angular/core';
import { FiltersComponent } from './filters/filters.component';
import { FilterListComponent } from './filter-list/filter-list.component';
import { FilterItemEditComponent } from './filter-item-edit/filter-item-edit.component';
import { FilterItemComponent } from './filter-item/filter-item.component';
@NgModule({
declarations: [],
imports: [
FiltersComponent, FilterListComponent
FiltersComponent,
FilterItemComponent,
FilterItemEditComponent,
FilterListComponent,
]
})
export class TtsFiltersModule { }

View File

@ -15,12 +15,18 @@
</mat-card-content>
<mat-card-actions class="actions">
<button mat-raised-button
<button mat-button
class="neutral"
disabled="{{waitForResponse}}"
(click)="dialogRef.close()">Cancel</button>
<button mat-raised-button
(click)="dialogRef.close()">
<mat-icon>cancel</mat-icon>Cancel
</button>
<button mat-button
class="confirm"
disabled="{{!usernameControl.dirty || usernameControl.invalid || waitForResponse}}"
(click)="submit()">Add</button>
(click)="submit()">
<mat-icon>add</mat-icon>Add
</button>
</mat-card-actions>
@if (responseError) {

View File

@ -14,7 +14,9 @@
rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
</head>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-typography">
<app-root></app-root>

View File

@ -1,21 +1,71 @@
/* You can add global styles to this file, and also import other style files */
@use "@angular/material" as mat;
@include mat.core();
@use '@angular/material' as mat;
html {
@include mat.theme((theme-type: light dark,
color: (primary: mat.$violet-palette,
tertiary: mat.$orange-palette,
use-system-variables: true,
),
typography: Roboto,
density: 0,
));
}
html,
body {
body,
main {
height: 100%;
}
html,
body,
main,
content,
nav {
background-color: var(--mat-sys-background);
color: var(--mat-sys-on-background);
}
.mat-mdc-card-header,
.mat-mdc-card-actions {
color: var(--mat-sys-on-background);
margin: 1em;
}
.mat-mdc-card-actions {
padding: 0;
margin: 0;
}
body {
margin: 0;
font-family: Roboto, "Helvetica Neue", sans-serif;
}
.light-theme {
color-scheme: light;
}
.dark-theme {
color-scheme: dark;
}
.mat-mdc-dialog-surface {
background-color: transparent !important;
}
.mat-mdc-card-actions {
display: flex;
align-items: center;
flex-direction: row;
justify-content: center;
}
.mat-mdc-button {
color: var(--mat-sys-on-background);
}
/* width */
::-webkit-scrollbar {
width: 5px;
@ -47,28 +97,28 @@ body {
}
.confirm {
@include mat.button-overrides((text-state-layer-color: rgb(52, 255, 62),
@include mat.button-overrides(( //text-state-layer-color: rgb(52, 255, 62),
text-label-text-color: rgb(71, 218, 78),
text-disabled-label-text-color: rgb(71, 218, 78),
));
}
.neutral {
@include mat.button-overrides((text-state-layer-color: rgb(64, 141, 255),
@include mat.button-overrides(( //text-state-layer-color: rgb(64, 141, 255),
text-label-text-color: rgb(52, 106, 255),
text-disabled-label-text-color: rgb(52, 106, 255),
));
}
.warning {
@include mat.button-overrides((text-state-layer-color: rgb(255, 172, 63),
@include mat.button-overrides(( //text-state-layer-color: rgb(255, 172, 63),
text-label-text-color: rgb(255, 145, 19),
text-disabled-label-text-color: rgb(255, 145, 19),
));
}
.danger {
@include mat.button-overrides((text-state-layer-color: rgb(255, 48, 48),
@include mat.button-overrides(( //text-state-layer-color: rgb(255, 48, 48),
text-label-text-color: rgb(255, 52, 52),
text-disabled-label-text-color: rgb(255, 52, 52),
filled-label-text-color: rgb(255, 52, 52),
@ -91,3 +141,18 @@ body {
align-self: center;
text-align: center;
}
@include mat.button-overrides((text-label-text-color: --mat-sys-on-primary,
protected-label-text-color: --mat-sys-on-primary,
//state-layer-color: --mat-sys-on-primary,
));
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, "Helvetica Neue", sans-serif;
}