Night theme (#2137)

* feat: add themes

* fix: remove obvious issues with light theme

* chore: improve light theme a bit

* comment out theme swticher

* chore: make login dark

* add theme and widgets to seeds

* add theme and widgets to migration

---------

Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
This commit is contained in:
Alex Inkin
2023-03-04 02:31:19 +08:00
committed by Aiden McClelland
parent e867f31c31
commit 3c0a82293c
53 changed files with 598 additions and 237 deletions

View File

@@ -2,6 +2,7 @@
button
*ngIf="pkg.entry.manifest as manifest"
detail="false"
class="service-card"
[routerLink]="['/services', manifest.id]"
>
<app-list-icon slot="start" [pkg]="pkg"></app-list-icon>
@@ -9,7 +10,7 @@
<img alt="" [src]="pkg.entry['static-files'].icon" />
</ion-thumbnail>
<ion-label>
<h2>{{ manifest.title }}</h2>
<h2 ticker>{{ manifest.title }}</h2>
<p>{{ manifest.version | displayEmver }}</p>
<status
[rendering]="pkg.primaryRendering"

View File

@@ -7,6 +7,7 @@ import {
EmverPipesModule,
ResponsiveColModule,
TextSpinnerComponentModule,
TickerModule,
} from '@start9labs/shared'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
@@ -37,6 +38,7 @@ const routes: Routes = [
BadgeMenuComponentModule,
WidgetListComponentModule,
ResponsiveColModule,
TickerModule,
],
declarations: [
AppListPage,

View File

@@ -3,10 +3,8 @@
<ion-buttons slot="start">
<ion-back-button defaultHref="services"></ion-back-button>
</ion-buttons>
<ion-item lines="none" color="light">
<ion-thumbnail slot="start">
<img [src]="pkg['static-files'].icon" alt="" />
</ion-thumbnail>
<div class="header">
<img class="logo" [src]="pkg['static-files'].icon" alt="" />
<ion-label>
<h1
class="montserrat"
@@ -16,6 +14,6 @@
</h1>
<h2>{{ pkg.manifest.version | displayEmver }}</h2>
</ion-label>
</ion-item>
</div>
</ion-toolbar>
</ion-header>

View File

@@ -1,3 +1,13 @@
.less-large {
font-size: 18px !important;
}
}
.header {
display: flex;
}
.logo {
height: 54px;
width: 54px;
margin: 0 16px;
}

View File

@@ -18,8 +18,8 @@ const routes: Routes = [
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild(routes),
SharedPipesModule,
RouterModule.forChild(routes),
],
declarations: [LoginPage],
})

View File

@@ -1,41 +1,35 @@
<ion-content>
<ion-grid style="height: 100%; max-width: 540px">
<ion-row class="ion-align-items-center" style="height: 90%">
<ion-col class="ion-text-center">
<div style="padding-bottom: 16px">
<img src="assets/img/logo.png" style="max-width: 240px" />
</div>
<ion-content class="content">
<ion-grid class="grid">
<ion-row class="row">
<ion-col>
<img src="assets/img/logo.png" alt="Start9" class="logo" />
<ion-card>
<ion-card-header class="ion-text-center" style="padding-bottom: 8px">
<ion-card-title>Embassy Login</ion-card-title>
<ion-card class="card">
<ion-card-header>
<ion-card-title class="title">Embassy Login</ion-card-title>
</ion-card-header>
<ion-card-content class="ion-margin ion-text-center">
<form
class="inline"
(submit)="submit()"
style="margin-bottom: 12px"
>
<ion-card-content class="ion-margin">
<form class="form" (submit)="submit()">
<ion-item-group>
<ion-item color="dark">
<ion-item color="light">
<ion-icon
slot="start"
name="key-outline"
style="margin-right: 16px"
></ion-icon>
<ion-input
[type]="unmasked ? 'text' : 'password'"
name="password"
placeholder="Password"
[type]="unmasked ? 'text' : 'password'"
[(ngModel)]="password"
(ionChange)="error = ''"
placeholder="Password"
></ion-input>
<ion-button fill="clear" color="light" (click)="toggleMask()">
<ion-button fill="clear" color="dark" (click)="toggleMask()">
<ion-icon
slot="icon-only"
[name]="unmasked ? 'eye-off-outline' : 'eye-outline'"
size="small"
[name]="unmasked ? 'eye-off-outline' : 'eye-outline'"
></ion-icon>
</ion-button>
</ion-item>
@@ -46,12 +40,12 @@
expand="block"
color="tertiary"
>
<span style="font-size: larger; font-weight: bold">Login</span>
Login
</ion-button>
<p style="text-align: left; padding-top: 4px">
<ion-text color="danger">{{ error }}</ion-text>
</p>
</form>
<p class="error">
<ion-text color="danger">{{ error }}</ion-text>
</p>
</ion-card-content>
</ion-card>
</ion-col>

View File

@@ -1,10 +1,37 @@
ion-card-title {
margin: 24px 0;
font-family: 'Montserrat';
font-weight: 500;
font-size: x-large;
font-variant: all-small-caps;
--color: var(--ion-color-dark);
.content {
--background: #222428;
}
.card {
background: #414141;
}
.title {
margin: 24px 0 16px;
color: #e0e0e0;
text-transform: uppercase;
}
.grid {
height: 100%;
max-width: 540px;
}
.row {
height: 90%;
align-items: center;
text-align: center;
}
.logo {
max-width: 240px;
padding-bottom: 16px;
}
.error {
display: block;
text-align: left;
padding-top: 4px;
}
ion-button {
@@ -15,8 +42,7 @@ ion-item {
--border-style: solid;
--border-color: var(--ion-color-light);
--border-radius: 4px 0 0 4px;
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
ion-button {
@@ -26,26 +52,22 @@ ion-item {
ion-card {
background: var(--ion-color-step-200);
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 44px;
min-height: 16rem;
}
.input-label {
text-align: left;
padding-bottom: 2px;
font-size: small;
font-weight: bold;
color: var(--ion-color-dark);
}
.login-button {
margin-inline-start: 0;
margin-inline-end: 0;
height: 49px;
font-size: larger;
font-weight: bold;
}
.inline {
.form {
margin-bottom: 12px;
* {
display: inline-block;
vertical-align: middle;
@@ -53,5 +75,5 @@ ion-card {
}
.item-interactive {
--highlight-background: var(--ion-color-tertiary) !important;
}
--highlight-background: #5260ff !important;
}

View File

@@ -8,6 +8,7 @@ import { TextSpinnerComponentModule } from '@start9labs/shared'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { OSUpdatePageModule } from 'src/app/modals/os-update/os-update.page.module'
import { BackupColorPipeModule } from 'src/app/pipes/backup-color/backup-color.module'
import { ThemeSwitcherModule } from '../theme-switcher/theme-switcher.module'
const routes: Routes = [
{
@@ -26,6 +27,7 @@ const routes: Routes = [
BadgeMenuComponentModule,
OSUpdatePageModule,
BackupColorPipeModule,
ThemeSwitcherModule,
],
declarations: [ServerShowPage],
})

View File

@@ -38,65 +38,65 @@
{{ cat.key }}
</ion-text>
</ion-item-divider>
<ng-container *ngFor="let button of cat.value">
<ion-item
button
[style.display]="(button.title === 'Repair Disk' && !(showDiskRepair$ | async)) ? 'none' : 'block'"
[detail]="button.detail"
[disabled]="button.disabled$ | async"
(click)="button.action()"
>
<ion-icon slot="start" [name]="button.icon"></ion-icon>
<ion-label>
<h2>{{ button.title }}</h2>
<p *ngIf="button.description">{{ button.description }}</p>
<!-- <theme-switcher *ngIf="cat.key === 'Manage'"></theme-switcher> -->
<ion-item
*ngFor="let button of cat.value"
button
[style.display]="(button.title === 'Repair Disk' && !(showDiskRepair$ | async)) ? 'none' : 'block'"
[detail]="button.detail"
[disabled]="button.disabled$ | async"
(click)="button.action()"
>
<ion-icon slot="start" [name]="button.icon"></ion-icon>
<ion-label>
<h2>{{ button.title }}</h2>
<p *ngIf="button.description">{{ button.description }}</p>
<!-- "Create Backup" button only -->
<p *ngIf="button.title === 'Create Backup'">
<ng-container *ngIf="server['status-info'] as statusInfo">
<ion-text
[color]="server['last-backup'] | backupColor"
*ngIf="!statusInfo['backup-progress'] && !statusInfo['update-progress']"
>
Last Backup: {{ server['last-backup'] ? (server['last-backup']
| date: 'medium') : 'never' }}
</ion-text>
<span *ngIf="!!statusInfo['backup-progress']" class="inline">
<ion-spinner
color="success"
style="height: 12px; width: 12px; margin-right: 6px"
></ion-spinner>
<ion-text color="success">Backing up</ion-text>
</span>
</ng-container>
</p>
<!-- "Software Update" button only -->
<p *ngIf="button.title === 'Software Update'">
<!-- "Create Backup" button only -->
<p *ngIf="button.title === 'Create Backup'">
<ng-container *ngIf="server['status-info'] as statusInfo">
<ion-text
*ngIf="server['status-info'].updated; else notUpdated"
class="inline"
color="warning"
[color]="server['last-backup'] | backupColor"
*ngIf="!statusInfo['backup-progress'] && !statusInfo['update-progress']"
>
Update Complete. Restart to apply changes
Last Backup: {{ server['last-backup'] ? (server['last-backup'] |
date: 'medium') : 'never' }}
</ion-text>
<ng-template #notUpdated>
<ng-container *ngIf="showUpdate$ | async; else check">
<ion-text class="inline" color="success">
<ion-icon name="rocket-outline"></ion-icon>
Update Available
</ion-text>
</ng-container>
<ng-template #check>
<ion-text class="inline" color="dark">
<ion-icon name="refresh"></ion-icon>
Check for updates
</ion-text>
</ng-template>
<span *ngIf="!!statusInfo['backup-progress']" class="inline">
<ion-spinner
color="success"
style="height: 12px; width: 12px; margin-right: 6px"
></ion-spinner>
<ion-text color="success">Backing up</ion-text>
</span>
</ng-container>
</p>
<!-- "Software Update" button only -->
<p *ngIf="button.title === 'Software Update'">
<ion-text
*ngIf="server['status-info'].updated; else notUpdated"
class="inline"
color="warning"
>
Update Complete. Restart to apply changes
</ion-text>
<ng-template #notUpdated>
<ng-container *ngIf="showUpdate$ | async; else check">
<ion-text class="inline" color="success">
<ion-icon name="rocket-outline"></ion-icon>
Update Available
</ion-text>
</ng-container>
<ng-template #check>
<ion-text class="inline" color="dark">
<ion-icon name="refresh"></ion-icon>
Check for updates
</ion-text>
</ng-template>
</p>
</ion-label>
</ion-item>
</ng-container>
</ng-template>
</p>
</ion-label>
</ion-item>
</div>
</ion-item-group>
</ion-content>

View File

@@ -56,7 +56,7 @@
{{ uploadState?.message }}
</h4>
<div class="box" *ngIf="toUpload.icon && toUpload.manifest">
<div class="service-card">
<div class="card">
<div class="row row_end">
<ion-button
style="

View File

@@ -53,7 +53,7 @@
justify-content: space-evenly
}
.service-card {
.card {
background: radial-gradient(var(--ion-color-step-100), transparent);
min-width: 200px;
max-width: 200px;

View File

@@ -0,0 +1,23 @@
<ion-item button [detail]="true" (click)="open = true">
<ion-icon slot="start" name="brush-outline"></ion-icon>
<ion-label>
<h2>Theme</h2>
<p>{{ value }}</p>
</ion-label>
</ion-item>
<ng-template
[(tuiDialog)]="open"
[tuiDialogOptions]="{
label: 'Select theme',
size: 's'
}"
>
<tui-radio-list
style="margin-top: 16px"
size="l"
[ngModel]="value"
[items]="themes"
(ngModelChange)="onChange($event)"
></tui-radio-list>
</ng-template>

View File

@@ -0,0 +1,23 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { ThemeSwitcherService } from '../../../services/theme-switcher.service'
@Component({
selector: 'theme-switcher',
templateUrl: './theme-switcher.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ThemeSwitcherComponent {
value = this.switcher.value
open = false
readonly themes = ['Dark', 'Light']
constructor(private readonly switcher: ThemeSwitcherService) {}
onChange(value: string): void {
this.value = value
this.switcher.next(value)
}
}

View File

@@ -0,0 +1,14 @@
import { NgModule } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { IonicModule } from '@ionic/angular'
import { TuiDialogModule } from '@taiga-ui/core'
import { TuiRadioListModule } from '@taiga-ui/kit'
import { ThemeSwitcherComponent } from './theme-switcher.component'
@NgModule({
imports: [IonicModule, FormsModule, TuiDialogModule, TuiRadioListModule],
declarations: [ThemeSwitcherComponent],
exports: [ThemeSwitcherComponent],
})
export class ThemeSwitcherModule {}

View File

@@ -57,7 +57,7 @@
.content {
height: 100%;
text-align: center;
background: #333;
background: var(--tui-base-02);
border-radius: 24px;
padding: 24px;
box-sizing: border-box;