feat: added app settings service to handle theme persistance, fix: optimised dark mode contrast
This commit is contained in:
parent
afa7e380aa
commit
35844e0cf1
@ -14,9 +14,9 @@ import { DcAdapterSettings } from './models/DcAdapterSettings'
|
||||
import { AppStoreService } from './services/app-store.service'
|
||||
import { LicenceService } from './services/licence.service'
|
||||
import '@cds/core/icon/register.js';
|
||||
import { ClarityIcons, moonIcon, sunIcon } from '@cds/core/icon';
|
||||
import { ClarityIcons, exclamationTriangleIcon, moonIcon, sunIcon } from '@cds/core/icon';
|
||||
|
||||
ClarityIcons.addIcons(moonIcon, sunIcon);
|
||||
ClarityIcons.addIcons(moonIcon, sunIcon, exclamationTriangleIcon);
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
|
@ -20,10 +20,10 @@ import { UsernavRouteComponent } from './routes/usernav-route/usernav-route.comp
|
||||
import { AppService } from './services/app.service'
|
||||
import { InfoModalComponent } from './shared/abort-modal/info-modal.component'
|
||||
import { RequestsModalComponent } from './shared/requests-modal/requests-modal.component'
|
||||
import { HomeModule } from './home/home.module'
|
||||
import { DirectivesModule } from './directives/directives.module'
|
||||
import { ViyaApiExplorerComponent } from './viya-api-explorer/viya-api-explorer.component'
|
||||
import { NgxJsonViewerModule } from 'ngx-json-viewer'
|
||||
import { AppSettingsService } from './services/app-settings.service'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -50,7 +50,7 @@ import { NgxJsonViewerModule } from 'ngx-json-viewer'
|
||||
DirectivesModule,
|
||||
NgxJsonViewerModule
|
||||
],
|
||||
providers: [AppService, SasStoreService, LicensingGuard],
|
||||
providers: [AppService, SasStoreService, LicensingGuard, AppSettingsService],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
@ -2769,7 +2769,6 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
||||
|
||||
this.currentEditRecordLoadings.push(column)
|
||||
hot.render()
|
||||
return
|
||||
|
||||
this.sasService
|
||||
.request('editors/getdynamiccolvals', data, undefined, {
|
||||
|
@ -11,9 +11,11 @@ export const errorRenderer = (
|
||||
value: any,
|
||||
cellProperties: any
|
||||
) => {
|
||||
addDarkClass(td)
|
||||
|
||||
td.innerHTML = `${
|
||||
value ? value.toString() : ''
|
||||
} <clr-icon shape="exclamation-circle" status="warning"></clr-icon>`
|
||||
} <cds-icon shape="exclamation-triangle" status="warning"></cds-icon>`
|
||||
|
||||
return td
|
||||
}
|
||||
@ -31,6 +33,8 @@ export const noSpinnerRenderer = (
|
||||
value: any,
|
||||
cellProperties: any
|
||||
) => {
|
||||
addDarkClass(td)
|
||||
|
||||
td.innerHTML = value ? value : ''
|
||||
|
||||
return td
|
||||
@ -50,10 +54,20 @@ export const spinnerRenderer = (
|
||||
value: any,
|
||||
cellProperties: any
|
||||
) => {
|
||||
td.classList.add('htDark')
|
||||
addDarkClass(td)
|
||||
|
||||
td.innerHTML = `${
|
||||
value ? value.toString() : ''
|
||||
} <span class="spinner spinner-sm vertical-align-middle"></span>`
|
||||
|
||||
return td
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a htDark class to a TD element if not existing
|
||||
*/
|
||||
const addDarkClass = (td: any) => {
|
||||
if (!td.classList.contains('htDark')) {
|
||||
td.classList.add('htDark')
|
||||
}
|
||||
}
|
9
client/src/app/models/AppSettings.ts
Normal file
9
client/src/app/models/AppSettings.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export interface AppSettings {
|
||||
persistSelectedTheme: boolean,
|
||||
selectedTheme: AppThemes
|
||||
}
|
||||
|
||||
export enum AppThemes {
|
||||
light = 'light',
|
||||
dark = 'dark'
|
||||
}
|
54
client/src/app/services/app-settings.service.ts
Normal file
54
client/src/app/services/app-settings.service.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { BehaviorSubject } from "rxjs"
|
||||
import { AppSettings, AppThemes } from "../models/AppSettings"
|
||||
|
||||
export class AppSettingsService {
|
||||
public defaultSettings: AppSettings = {
|
||||
persistSelectedTheme: true,
|
||||
selectedTheme: AppThemes.light
|
||||
}
|
||||
public settings = new BehaviorSubject<AppSettings>(this.defaultSettings)
|
||||
|
||||
constructor() {
|
||||
this.restoreAppSettings()
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore app settings from the local storage
|
||||
*/
|
||||
restoreAppSettings() {
|
||||
try {
|
||||
const serializedSettings = localStorage.getItem('app-settings')
|
||||
|
||||
if (serializedSettings) {
|
||||
const settings = JSON.parse(serializedSettings)
|
||||
|
||||
this.setAppSettings(settings)
|
||||
} else {
|
||||
console.info('No app settings stored in the localStorage, we will set to default values.')
|
||||
}
|
||||
} catch(err) {
|
||||
console.warn('Error restoring settings from local storgae.', err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save app settings to the local storage
|
||||
*/
|
||||
storeAppSettings() {
|
||||
localStorage.setItem('app-settings', JSON.stringify(this.settings.value))
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used in the app to update global settings object
|
||||
*
|
||||
* @param appSettings contains properties that should be updated in settings
|
||||
*/
|
||||
setAppSettings(appSettings: Partial<AppSettings>) {
|
||||
this.settings.next({
|
||||
...this.settings.value,
|
||||
...appSettings
|
||||
})
|
||||
|
||||
this.storeAppSettings()
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@ import { BehaviorSubject } from 'rxjs'
|
||||
import { LoggerService } from './logger.service'
|
||||
import { EnvironmentInfo } from '../system/models/environment-info.model'
|
||||
import { LicenceService } from './licence.service'
|
||||
import { AppSettingsService } from './app-settings.service'
|
||||
import { AppThemes } from '../models/AppSettings'
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
@ -18,6 +20,7 @@ export class AppService {
|
||||
private eventService: EventService,
|
||||
private sasService: SasService,
|
||||
private loggerService: LoggerService,
|
||||
private appSettingsService: AppSettingsService,
|
||||
private router: Router
|
||||
) {
|
||||
this.subscribe()
|
||||
@ -29,6 +32,19 @@ export class AppService {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const appSettings = this.appSettingsService.settings.value
|
||||
|
||||
if (!!appSettings.persistSelectedTheme) {
|
||||
if (appSettings.selectedTheme === AppThemes.light) {
|
||||
this.eventService.toggleDarkMode(false)
|
||||
} else if (appSettings.selectedTheme === AppThemes.dark) {
|
||||
this.eventService.toggleDarkMode(true)
|
||||
} else {
|
||||
// Fallback to light mode
|
||||
this.eventService.toggleDarkMode(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sasServiceInit() {
|
||||
|
@ -2,6 +2,8 @@ import { Injectable, Output, EventEmitter } from '@angular/core'
|
||||
import { InfoModal, AbortDetails } from '../models/InfoModal'
|
||||
import { AlertsService } from '../shared/alerts/alerts.service'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { AppSettingsService } from './app-settings.service'
|
||||
import { AppThemes } from '../models/AppSettings'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -20,7 +22,9 @@ export class EventService {
|
||||
|
||||
public darkMode: BehaviorSubject<boolean> = new BehaviorSubject(false)
|
||||
|
||||
constructor(private alertsService: AlertsService) {}
|
||||
constructor(
|
||||
private appSettingsService: AppSettingsService
|
||||
) {}
|
||||
|
||||
toggleDarkMode(value: boolean) {
|
||||
this.darkMode.next(value)
|
||||
@ -30,6 +34,10 @@ export class EventService {
|
||||
} else {
|
||||
document.body.setAttribute('cds-theme', 'light')
|
||||
}
|
||||
|
||||
this.appSettingsService.setAppSettings({
|
||||
selectedTheme: value ? AppThemes.dark : AppThemes.light
|
||||
})
|
||||
}
|
||||
|
||||
public showDemoLimitModal(featureName: string) {
|
||||
|
@ -12,4 +12,10 @@
|
||||
cursor:pointer;
|
||||
margin-top:10px;
|
||||
color: #007cbb;
|
||||
}
|
||||
|
||||
::ng-deep body[cds-theme="dark"] {
|
||||
.baseTableLink {
|
||||
color: #4ec0ff;
|
||||
}
|
||||
}
|
@ -194,6 +194,16 @@
|
||||
|
||||
<hr class="w-100 light" />
|
||||
|
||||
<!-- Keep the logic in case we in future have more then 2 themes (light, dark) -->
|
||||
<!-- -->
|
||||
<!-- <div class="user-action">
|
||||
Keep selected theme after reload
|
||||
|
||||
<clr-checkbox-wrapper>
|
||||
<input [(ngModel)]="settings.persistSelectedTheme" (change)="settingChange($event)" value="persistDarkMode" type="checkbox" clrToggle />
|
||||
</clr-checkbox-wrapper>
|
||||
</div> -->
|
||||
|
||||
<ng-container *ngIf="environmentInfo?.ISADMIN === 1">
|
||||
<div *ngIf="serverType === 'SAS9'" class="admin-action">
|
||||
Refresh Data Lineage
|
||||
|
@ -12,7 +12,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.admin-action {
|
||||
.admin-action, .user-action {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
@ -7,6 +7,8 @@ import { LicenceService } from '../services/licence.service'
|
||||
import { SasService } from '../services/sas.service'
|
||||
import { AppInfo } from './models/app-info.model'
|
||||
import { EnvironmentInfo } from './models/environment-info.model'
|
||||
import { AppSettingsService } from '../services/app-settings.service'
|
||||
import { AppSettings } from '../models/AppSettings'
|
||||
|
||||
@Component({
|
||||
selector: 'app-system',
|
||||
@ -36,25 +38,36 @@ export class SystemComponent implements OnInit {
|
||||
Infinity = Infinity
|
||||
|
||||
licenceState = this.licenceService.licenceState
|
||||
settings: AppSettings
|
||||
|
||||
constructor(
|
||||
private appService: AppService,
|
||||
private sasService: SasService,
|
||||
private licenceService: LicenceService
|
||||
private licenceService: LicenceService,
|
||||
private appSettingsService: AppSettingsService
|
||||
) {
|
||||
this.serverType = this.sasService.getServerType()
|
||||
this.licenceInfo = this.licenceService.getLicenseKeyData()
|
||||
this.environmentInfo = this.appService.getEnvironmentInfo()
|
||||
this.settings = this.appSettingsService.settings.value
|
||||
|
||||
if (this.environmentInfo) {
|
||||
this.environmentInfo.AUTOEXEC = decodeURIComponent(
|
||||
this.environmentInfo.AUTOEXEC
|
||||
)
|
||||
}
|
||||
|
||||
this.appSettingsService.settings.subscribe((settings: AppSettings) => {
|
||||
this.settings = settings
|
||||
})
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
settingChange(event: Event) {
|
||||
this.appSettingsService.setAppSettings(this.settings)
|
||||
}
|
||||
|
||||
downloadConfiguration() {
|
||||
let sasjsConfig = this.sasService.getSasjsConfig()
|
||||
let storage = sasjsConfig.serverUrl
|
||||
|
@ -4,9 +4,15 @@ import { CommonModule } from '@angular/common'
|
||||
import { SystemRoutingModule } from './system-routing.module'
|
||||
import { SystemComponent } from './system.component'
|
||||
import { ClarityModule } from '@clr/angular'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
|
||||
@NgModule({
|
||||
declarations: [SystemComponent],
|
||||
imports: [CommonModule, SystemRoutingModule, ClarityModule]
|
||||
imports: [
|
||||
CommonModule,
|
||||
SystemRoutingModule,
|
||||
ClarityModule,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class SystemModule {}
|
||||
|
@ -33,6 +33,10 @@ body[cds-theme="dark"] {
|
||||
// Track
|
||||
border: 3px solid $trackColor;
|
||||
}
|
||||
|
||||
clr-icon.is-highlight {
|
||||
fill: #4ec0ff;
|
||||
}
|
||||
}
|
||||
|
||||
// body::-webkit-scrollbar {
|
||||
|
14
sas/package-lock.json
generated
14
sas/package-lock.json
generated
@ -7,7 +7,7 @@
|
||||
"name": "dc-sas",
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "^4.11.1",
|
||||
"@sasjs/core": "^4.52.1"
|
||||
"@sasjs/core": "^4.52.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@coolaj86/urequest": {
|
||||
@ -116,9 +116,9 @@
|
||||
"integrity": "sha512-Grwydm5GxBsYk238PZw41XPjXVVQ9vWcvfZ06L2P0bQbvK0sGn7l69JA7H5MGr3QcaLpiD4Kg70cAh7PgE+JOw=="
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
"version": "4.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.1.tgz",
|
||||
"integrity": "sha512-XPNuKD1T5XLGMKg4Ll3KggOTjhHgnjdbefpwajpfro/8/9bJK7lyNehzUCcmbhJnijJbbChE7drIOF+uSaVxVg=="
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.4.tgz",
|
||||
"integrity": "sha512-8lf5ixlA312EgA2DorwbpNXXPfLPzUHO67exIV7SjKiU23Tn1au5GD6hT0Ysr2kophOs10Mp1TCXJjhEq7Qk4A=="
|
||||
},
|
||||
"node_modules/@sasjs/lint": {
|
||||
"version": "2.3.1",
|
||||
@ -1834,9 +1834,9 @@
|
||||
}
|
||||
},
|
||||
"@sasjs/core": {
|
||||
"version": "4.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.1.tgz",
|
||||
"integrity": "sha512-XPNuKD1T5XLGMKg4Ll3KggOTjhHgnjdbefpwajpfro/8/9bJK7lyNehzUCcmbhJnijJbbChE7drIOF+uSaVxVg=="
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.52.4.tgz",
|
||||
"integrity": "sha512-8lf5ixlA312EgA2DorwbpNXXPfLPzUHO67exIV7SjKiU23Tn1au5GD6hT0Ysr2kophOs10Mp1TCXJjhEq7Qk4A=="
|
||||
},
|
||||
"@sasjs/lint": {
|
||||
"version": "2.3.1",
|
||||
|
@ -29,6 +29,6 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@sasjs/cli": "^4.11.1",
|
||||
"@sasjs/core": "^4.52.1"
|
||||
"@sasjs/core": "^4.52.4"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user