UNPKG

@fleetbase/ember-core

Version:

Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.

250 lines (221 loc) 6.34 kB
import Service from '@ember/service'; import Evented from '@ember/object/evented'; import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { dasherize } from '@ember/string'; import { isArray } from '@ember/array'; import { getOwner } from '@ember/application'; import { debug } from '@ember/debug'; export default class ThemeService extends Service.extend(Evented) { /** * Lookup the correct router for the engine and or console. * * @readonly * @memberof ThemeService */ get router() { const owner = getOwner(this); const router = owner.lookup('router:main'); return router; } /** * Inject the current user service * * @var {Service} */ @service currentUser; /** * The current active theme, uses the system preffered mode to set default * refers to the theme in headData service * * @var {String} */ get activeTheme() { const userSetTheme = this.currentUser.getOption('theme'); if (userSetTheme) { return userSetTheme; } if (this.initialTheme) { return this.initialTheme; } if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return 'dark'; } // default to dark theme return this.currentTheme; } /** * Set the activeTheme in the headData service */ set activeTheme(theme) { document.body.dataset.theme = theme; this.currentTheme = theme; } /** * Current theme, defaults to dark, active theme represents the theme set by user OS * * @var {String} */ @tracked currentTheme = this.currentUser.getOption('theme', 'dark'); /** * The initially set theme * * @var {String} */ @tracked initialTheme; /** * The current route name as style class * * @var {String} */ get routeClassName() { return dasherize(typeof this.router.currentRouteName === 'string' ? this.router.currentRouteName.replace(/\./g, ' ') : 'fleetbase-console'); } /** * The current route * * @var {Route} */ get currentRoute() { return getOwner(this).lookup(`route:${this.router.currentRouteName}`); } /** * The current route * * @var {Route} */ get currentRouteBodyClasses() { if (this.currentRoute && this.currentRoute.bodyClassNames && isArray(this.currentRoute.bodyClassNames)) { return this.currentRoute.bodyClassNames; } return []; } /** * Hook for handling when route does change * * @void */ routeDidChange() { this.setRoutebodyClassNames(this.currentRouteBodyClasses); } /** * Hook for handling when route does change * * @void */ routeWillChange() { this.removeRoutebodyClassNames(this.currentRouteBodyClasses); } /** * Initialize theme configurations * * @void */ initialize(options = {}) { this.initialTheme = options?.theme; this.applyTheme(this.activeTheme, { persist: false }); this.setEnvironment(); this.resetScroll(); this.setRoutebodyClassNames(options.bodyClassNames && isArray(options.bodyClassNames) ? options.bodyClassNames : []); // on every subsequent transition set the body class this.router.on('routeDidChange', this.routeDidChange.bind(this)); // remove route class as exiting this.router.on('routeWillChange', this.routeWillChange.bind(this)); // remove console-loader this.removeConsoleLoader(); // run a `onInit` callback if provided if (typeof options.onInit === 'function') { options.onInit(this); } } /** * Remove the console-loader * * @memberof ThemeService */ removeConsoleLoader() { const consoleLoader = document.getElementById(`console-loader`); if (consoleLoader) { consoleLoader.remove(); } } /** * Resets window scroll * * @void */ resetScroll() { window.scrollTo(0, 0); } /** * Appends the current route name to body * * @void */ setRoutebodyClassNames(classes = []) { document.body.classList.add(...[this.routeClassName, `${this.currentTheme}-theme`, ...classes]); } /** * Remove thes current route name from body * * @void */ removeRoutebodyClassNames(classes = []) { document.body.classList.remove(...[this.routeClassName, `${this.currentTheme}-theme`, ...classes]); } /** * Toggle the activeTheme between light and dark, and returns the toggled them * * @return string */ toggleTheme() { const nextTheme = this.currentTheme === 'light' ? 'dark' : 'light'; this.setTheme(nextTheme); return nextTheme; } /** * Set the theme to the headData * * @void */ setTheme(theme = 'light') { this.applyTheme(theme); } /** * Synchronize the active theme from the current user-scoped preference. * * @void */ syncThemeFromCurrentUser() { this.applyTheme(this.activeTheme, { persist: false }); } /** * Apply a theme consistently to service state and document body. * * @void */ applyTheme(theme = 'light', options = {}) { const persist = options.persist !== false; debug(`Theme was changed to: ${theme}`); document.body.classList.remove('light-theme', 'dark-theme'); document.body.classList.add(`${theme}-theme`); if (persist) { this.currentUser.setOption('theme', theme); } this.activeTheme = theme; this.trigger('theme.changed', theme); } /** * Set the theme to the headData * * @void */ setEnvironment() { const isSandbox = this.currentUser.getOption('sandbox', false); if (isSandbox) { document.body.classList.add('sandbox-console'); } else { document.body.classList.remove('sandbox-console'); } } }