@marcj/angular-desktop-ui
Version:
library offering you desktop UI widgets in Angular 7+
289 lines (255 loc) • 8.69 kB
text/typescript
import {
ApplicationRef,
Component,
HostBinding,
Inject,
Injectable,
Input,
ModuleWithProviders,
NgModule,
Optional,
Directive
} from "@angular/core";
import {
MenuCheckboxDirective,
MenuDirective,
MenuItemDirective,
MenuRadioDirective,
MenuSeparatorDirective
} from "./menu.component";
import {detectChangesNextFrame, OpenExternalDirective, ZonelessChangeDetector} from "./utils";
import {ViewDirective} from "./dui-view.directive";
import {CdCounterComponent} from "./cd-counter.component";
import {DuiResponsiveDirective} from "./dui-responsive.directive";
import {CommonModule, DOCUMENT} from "@angular/common";
import {Electron} from "../../core/utils";
import {ActivationEnd, Event as RouterEvent, NavigationEnd, Router} from "@angular/router";
import {WindowRegistry} from "../window/window-state";
import {ELECTRON_WINDOW, IN_DIALOG} from "./token";
import {AsyncRenderPipe} from "./pipes";
export * from "./cd-counter.component";
export * from "./dui-view.directive";
export * from "./dui-responsive.directive";
export * from "./utils";
export * from "./menu.component";
export * from "./pipes";
if ('undefined' !== typeof window && 'undefined' === typeof (window as any)['global']) {
(window as any).global = window;
}
()
export class BaseComponent {
() disabled?: boolean;
('class.disabled')
get isDisabled() {
return this.disabled === true;
}
}
({
selector: 'ui-component',
template: `
{{name}} disabled={{isDisabled}}
`,
styles: [`
:host {
display: inline-block;
}
:host.disabled {
border: 1px solid red;
}
`],
host: {
'[class.is-textarea]': 'name === "textarea"',
}
})
export class UiComponentComponent extends BaseComponent {
() name: string = '';
}
()
export class DuiApp {
protected darkMode?: boolean = false;
protected platform: 'web' | 'darwin' | 'linux' | 'win32' = 'darwin';
constructor(
protected app: ApplicationRef,
protected windowRegistry: WindowRegistry,
() protected router?: Router
) {
ZonelessChangeDetector.app = app;
if ('undefined' !== typeof window) {
(window as any)['DuiApp'] = this;
}
}
start() {
if (Electron.isAvailable()) {
document.body.classList.add('electron');
const remote = Electron.getRemote();
let overwrittenDarkMode = localStorage.getItem('duiApp/darkMode');
if (overwrittenDarkMode) {
this.setDarkMode(JSON.parse(overwrittenDarkMode));
} else {
this.setDarkMode();
}
this.setPlatform(remote.process.platform);
} else {
this.setPlatform('web');
this.setDarkMode();
}
const mm = window.matchMedia('(prefers-color-scheme: dark)');
const setTheme = () => {
if (localStorage.getItem('duiApp/darkMode') === null) {
this.setAutoDarkMode();
this.app.tick();
}
};
if (mm.addEventListener) {
mm.addEventListener('change', setTheme);
} else {
//ios
mm.addListener(setTheme);
}
if ('undefined' !== typeof document) {
document.addEventListener('click', () => detectChangesNextFrame());
document.addEventListener('focus', () => detectChangesNextFrame());
document.addEventListener('blur', () => detectChangesNextFrame());
document.addEventListener('keydown', () => detectChangesNextFrame());
document.addEventListener('keyup', () => detectChangesNextFrame());
document.addEventListener('keypress', () => detectChangesNextFrame());
document.addEventListener('mousedown', () => detectChangesNextFrame());
}
//necessary to render all router-outlet once the router changes
if (this.router) {
this.router.events.subscribe((event: RouterEvent) => {
if (event instanceof NavigationEnd || event instanceof ActivationEnd) {
detectChangesNextFrame();
}
});
}
}
setPlatform(platform: 'web' | 'darwin' | 'linux' | 'win32') {
this.platform = platform;
document.body.classList.remove('platform-linux');
document.body.classList.remove('platform-darwin');
document.body.classList.remove('platform-win32');
document.body.classList.remove('platform-native');
document.body.classList.remove('platform-web');
if (this.platform !== 'web') {
document.body.classList.add('platform-native');
}
document.body.classList.add('platform-' + platform);
}
getPlatform(): string {
return this.platform;
}
isDarkMode() {
return this.darkMode;
}
setAutoDarkMode() {
this.setDarkMode();
}
get theme(): 'auto' | 'light' | 'dark' {
if (this.isDarkModeOverwritten()) {
return this.isDarkMode() ? 'dark' : 'light';
}
return 'auto';
}
set theme(theme: 'auto' | 'light' | 'dark') {
if (theme === 'auto') {
this.setAutoDarkMode();
return;
}
this.setDarkMode(theme === 'dark');
}
isDarkModeOverwritten() {
return localStorage.getItem('duiApp/darkMode') !== null;
}
setGlobalDarkMode(darkMode: boolean) {
if (Electron.isAvailable()) {
const remote = Electron.getRemote();
for (const win of remote.BrowserWindow.getAllWindows()) {
win.webContents.executeJavaScript(`DuiApp.setDarkMode(${darkMode})`);
}
}
}
getVibrancy(): 'ultra-dark' | 'light' {
return this.darkMode ? 'ultra-dark' : 'light';
}
setDarkMode(darkMode?: boolean) {
if (darkMode === undefined) {
this.darkMode = this.isPreferDarkColorSchema();
localStorage.removeItem('duiApp/darkMode');
} else {
localStorage.setItem('duiApp/darkMode', JSON.stringify(darkMode));
this.darkMode = darkMode;
}
for (const win of this.windowRegistry.getAllElectronWindows()) {
win.setVibrancy(this.getVibrancy());
}
document.body.classList.remove('dark');
document.body.classList.remove('light');
document.body.classList.add(this.darkMode ? 'dark' : 'light');
window.dispatchEvent(new Event('theme-changed'));
}
protected isPreferDarkColorSchema() {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
}
({
declarations: [
UiComponentComponent,
MenuDirective,
MenuSeparatorDirective,
MenuRadioDirective,
MenuCheckboxDirective,
MenuItemDirective,
OpenExternalDirective,
ViewDirective,
CdCounterComponent,
DuiResponsiveDirective,
AsyncRenderPipe,
],
exports: [
UiComponentComponent,
MenuDirective,
MenuSeparatorDirective,
MenuRadioDirective,
MenuCheckboxDirective,
MenuItemDirective,
OpenExternalDirective,
ViewDirective,
CdCounterComponent,
DuiResponsiveDirective,
AsyncRenderPipe,
],
imports: [
CommonModule,
]
})
export class DuiAppModule {
constructor(app: DuiApp, (DOCUMENT) () document: Document) {
app.start();
if (document && Electron.isAvailable()) {
document.addEventListener('click', (event: MouseEvent) => {
if (event.target) {
const target = event.target as HTMLElement;
if (target.tagName.toLowerCase() === 'a') {
event.preventDefault();
event.stopPropagation();
Electron.getRemote().shell.openExternal((target as any).href);
}
}
});
}
}
static forRoot(): ModuleWithProviders<DuiAppModule> {
return {
ngModule: DuiAppModule,
providers: [
DuiApp,
{provide: IN_DIALOG, useValue: false},
{
provide: ELECTRON_WINDOW,
useValue: Electron.isAvailable() ? Electron.getRemote().BrowserWindow.getAllWindows()[0] : undefined
},
]
}
}
}