@v4fire/client
Version:
V4Fire client core library
372 lines (304 loc) • 8.72 kB
text/typescript
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/
/**
* [[include:super/i-static-page/README.md]]
* @packageDocumentation
*/
import symbolGenerator from 'core/symbol';
import { RestrictedCache } from 'core/cache';
import { setI18NParam, locale, region } from 'core/i18n';
import { reset, ResetType } from 'core/component';
import type bRouter from 'base/b-router/b-router';
import type { AppliedRoute } from 'core/router';
import type iBlock from 'super/i-block/i-block';
import iPage, { component, field, system, computed, watch } from 'super/i-page/i-page';
import createProviderDataStore, { ProviderDataStore } from 'super/i-static-page/modules/provider-data-store';
import themeManagerFactory, { ThemeManager } from 'super/i-static-page/modules/theme';
import PageMetaData from 'super/i-static-page/modules/page-meta-data';
import type { RootMod } from 'super/i-static-page/interface';
export * from 'super/i-page/i-page';
export * from 'super/i-static-page/modules/theme';
export { createProviderDataStore };
export * from 'super/i-static-page/modules/provider-data-store';
export * from 'super/i-static-page/interface';
export const
$$ = symbolGenerator();
/**
* Superclass for all root components
*/
export default abstract class iStaticPage extends iPage {
/**
* Type: page parameters
*/
readonly PageParams!: this['Router']['PageParams'];
/**
* Type: page query
*/
readonly PageQuery!: this['Router']['PageQuery'];
/**
* Type: page meta
*/
readonly PageMeta!: this['Router']['PageMeta'];
/**
* Type: router
*/
readonly Router!: bRouter;
/**
* Type: current page
*/
readonly CurrentPage!: AppliedRoute<this['PageParams'], this['PageQuery'], this['PageMeta']>;
/**
* Module to work with data of data providers globally
*/
readonly providerDataStore!: ProviderDataStore;
/**
* Module to manage app themes from the Design System
*/
<iStaticPage>(themeManagerFactory)
readonly theme: CanUndef<ThemeManager>;
/**
* Module to work with metadata of page
*/
readonly PageMetaData!: PageMetaData;
/**
* True if the current user is authorized
*/
isAuth!: boolean;
/**
* True if there is a connection to the Internet
*/
isOnline!: boolean;
/**
* Last date when the application was online
*/
lastOnlineDate?: Date;
/**
* Name of the active route page
*/
get activePage(): CanUndef<string> {
return this.field.get('route.meta.name');
}
override get route(): CanUndef<this['CurrentPage']> {
return this.field.get('routeStore');
}
/**
* Sets a new route object
*
* @param value
* @emits `setRoute(value: CanUndef<this['CurrentPage']>)`
*/
override set route(value: CanUndef<this['CurrentPage']>) {
this.field.set('routeStore', value);
this.emit('setRoute', value);
}
/**
* System locale
*/
get locale(): Language {
return this.field.get<Language>('localeStore')!;
}
/**
* Sets a new system locale
*/
set locale(value: Language) {
this.field.set('localeStore', value);
try {
document.documentElement.setAttribute('lang', value);
} catch {}
setI18NParam('locale', value);
}
/**
* System region
*/
get region(): CanUndef<Region> {
return this.field.get<CanUndef<Region>>('regionStore');
}
/**
* Sets a new system locale
*/
set region(value: CanUndef<Region>) {
this.field.set('regionStore', value);
setI18NParam('region', value);
}
/**
* Route information object store
* @see [[iStaticPage.route]]
*/
protected routeStore?: this['CurrentPage'];
/**
* Link to a router instance
*/
protected routerStore?: this['Router'];
/** @see [[iStaticPage.locale]] */
protected localeStore!: Language;
/** @see [[iStaticPage.region]] */
protected regionStore: CanUndef<Region> = region.value;
/**
* Cache of root modifiers
*/
protected rootMods: Dictionary<RootMod> = {};
/**
* Sends a message to reset data of all components.
* The method can take a reset type:
*
* 1. `'load'` - reload provider' data of all components;
* 2. `'load.silence'` - reload provider' data of all components without triggering of component' state;
* 3. `'router'` - reload router' data of all components;
* 4. `'router.silence'` - reload router' data of all components without triggering of component' state;
* 5. `'storage'` - reload storage' data of all components;
* 6. `'storage.silence'` - reload storage' data of all components without triggering of component' state;
* 7. `'silence'` - reload all components without triggering of component' state.
*
* @param [type] - reset type
*/
reset(type?: ResetType): void {
this.nextTick(() => reset(type), {
label: $$.reset
});
}
/**
* @param name
* @param value
* @param [component] - instance of the component that wants to set a modifier
*/
override setRootMod(name: string, value: unknown, component: iBlock = this): boolean {
const
root = document.documentElement;
if (value === undefined || !Object.isTruly(root)) {
return false;
}
const
cl = root.classList,
globalName = (component.globalName ?? component.componentName).dasherize();
const
modKey = this.getRootModKey(name, component),
modClass = this.provide.fullComponentName(globalName, name, value).replace(/_/g, '-');
const
normalizedValue = String(value).dasherize(),
cache = this.rootMods[modKey];
if (cache) {
if (cache.value === normalizedValue && cache.component === component) {
return false;
}
cl.remove(cache.class);
}
cl.add(modClass);
this.rootMods[modKey] = {
name: name.dasherize(),
value: normalizedValue,
class: modClass,
component
};
return true;
}
/**
* @param name
* @param [value]
* @param [component] - instance of the component that wants to remove a modifier
*/
override removeRootMod(name: string, value?: unknown, component: iBlock = this): boolean {
const
root = document.documentElement;
if (!Object.isTruly(root)) {
return false;
}
const
modKey = this.getRootModKey(name, component),
cache = this.rootMods[modKey];
if (cache) {
if (cache.component !== component) {
return false;
}
const
normalizedValue = value !== undefined ? String(value).dasherize() : undefined;
if (normalizedValue === undefined || normalizedValue === cache.value) {
root.classList.remove(cache.class);
delete this.rootMods[modKey];
return true;
}
}
return false;
}
/**
* @param name
* @param [component] - instance of the component that wants to get a modifier
*/
override getRootMod(name: string, component: iBlock = this): CanUndef<string> {
return this.rootMods[this.getRootModKey(name, component)]?.value;
}
/**
* Returns a key to save the specified root element modifier
*
* @param name - modifier name
* @param [component]
*/
protected getRootModKey(name: string, component: iBlock = this): string {
return `${(component.globalName ?? component.componentName).dasherize()}_${name.camelize(false)}`;
}
/**
* Synchronization of the `localeStore` field
* @param locale
*/
protected syncLocaleWatcher(locale: Language): void {
if (this.locale === locale) {
return;
}
this.locale = locale;
this.forceUpdate().catch(stderr);
}
/**
* Synchronization of the `regionStore` field
* @param region
*/
protected syncRegionWatcher(region: Region): void {
if (this.region === region) {
return;
}
this.region = region;
this.forceUpdate().catch(stderr);
}
/**
* Handler: the online status has been changed
* @param status
*/
protected onOnlineChange(status: string): void {
this.setRootMod('online', status);
}
}