Files
blockly/core/theme_manager.ts
Maribeth Bottorff 037eb59b89 chore: Lint TsDoc. (#6353)
* chore: add linting for tsdoc

* chore: don't require types on return

* chore: remove redundant fileoverview from ts

* chore: change return to returns and add some newlines

* chore: remove license tag

* chore: don't require params/return docs

* chore: remove spurious struct tags

* Revert "chore: change return to returns and add some newlines"

This reverts commit d6d8656a45.

* chore: don't auto-add param names

* chore: disable require-param bc it breaks on this

* return to returns and add line breaks

* chore: configure additional jsdoc rules

* chore: run format

* Revert "chore: remove license tag"

This reverts commit 173455588a.

* chore: allow license tag format

* chore: only require jsdoc on exported items

* chore: add missing jsdoc or silence where needed

* chore: run format

* chore: lint fixes
2022-08-23 14:27:22 -07:00

191 lines
5.5 KiB
TypeScript

/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Object in charge of storing and updating a workspace theme
* and UI components.
*
* @class
*/
import * as goog from '../closure/goog/goog.js';
goog.declareModuleId('Blockly.ThemeManager');
import type {Theme} from './theme.js';
import * as arrayUtils from './utils/array.js';
import * as dom from './utils/dom.js';
import type {Workspace} from './workspace.js';
import type {WorkspaceSvg} from './workspace_svg.js';
/**
* Class for storing and updating a workspace's theme and UI components.
*
* @alias Blockly.ThemeManager
*/
export class ThemeManager {
/** A list of workspaces that are subscribed to this theme. */
private subscribedWorkspaces_: Workspace[] = [];
private componentDB = new Map<string, Component[]>();
owner_: AnyDuringMigration;
/**
* @param workspace The main workspace.
* @param theme The workspace theme.
* @internal
*/
constructor(private readonly workspace: WorkspaceSvg, private theme: Theme) {}
/**
* Get the workspace theme.
*
* @returns The workspace theme.
* @internal
*/
getTheme(): Theme {
return this.theme;
}
/**
* Set the workspace theme, and refresh the workspace and all components.
*
* @param theme The workspace theme.
* @internal
*/
setTheme(theme: Theme) {
const prevTheme = this.theme;
this.theme = theme;
// Set the theme name onto the injection div.
const injectionDiv = this.workspace.getInjectionDiv();
if (injectionDiv) {
if (prevTheme) {
dom.removeClass(injectionDiv, prevTheme.getClassName());
}
dom.addClass(injectionDiv, this.theme.getClassName());
}
// Refresh all subscribed workspaces.
for (let i = 0, workspace; workspace = this.subscribedWorkspaces_[i]; i++) {
(workspace as WorkspaceSvg).refreshTheme();
}
// Refresh all registered Blockly UI components.
for (const [key, componentList] of this.componentDB) {
for (const component of componentList) {
const element = component.element;
const propertyName = component.propertyName;
const style = this.theme && this.theme.getComponentStyle(key);
// AnyDuringMigration because: Property 'style' does not exist on type
// 'Element'.
(element as AnyDuringMigration).style[propertyName] = style || '';
}
}
for (const workspace of this.subscribedWorkspaces_) {
(workspace as WorkspaceSvg).hideChaff();
}
}
/**
* Subscribe a workspace to changes to the selected theme. If a new theme is
* set, the workspace is called to refresh its blocks.
*
* @param workspace The workspace to subscribe.
* @internal
*/
subscribeWorkspace(workspace: Workspace) {
this.subscribedWorkspaces_.push(workspace);
}
/**
* Unsubscribe a workspace to changes to the selected theme.
*
* @param workspace The workspace to unsubscribe.
* @internal
*/
unsubscribeWorkspace(workspace: Workspace) {
if (!arrayUtils.removeElem(this.subscribedWorkspaces_, workspace)) {
throw Error(
'Cannot unsubscribe a workspace that hasn\'t been subscribed.');
}
}
/**
* Subscribe an element to changes to the selected theme. If a new theme is
* selected, the element's style is refreshed with the new theme's style.
*
* @param element The element to subscribe.
* @param componentName The name used to identify the component. This must be
* the same name used to configure the style in the Theme object.
* @param propertyName The inline style property name to update.
* @internal
*/
subscribe(element: Element, componentName: string, propertyName: string) {
if (!this.componentDB.has(componentName)) {
this.componentDB.set(componentName, []);
}
// Add the element to our component map.
this.componentDB.get(componentName)!.push({element, propertyName});
// Initialize the element with its corresponding theme style.
const style = this.theme && this.theme.getComponentStyle(componentName);
// AnyDuringMigration because: Property 'style' does not exist on type
// 'Element'.
(element as AnyDuringMigration).style[propertyName] = style || '';
}
/**
* Unsubscribe an element to changes to the selected theme.
*
* @param element The element to unsubscribe.
* @internal
*/
unsubscribe(element: Element) {
if (!element) {
return;
}
// Go through all component, and remove any references to this element.
for (const [componentName, elements] of this.componentDB) {
for (let i = elements.length - 1; i >= 0; i--) {
if (elements[i].element === element) {
elements.splice(i, 1);
}
}
// Clean up the component map entry if the list is empty.
if (!elements.length) {
this.componentDB.delete(componentName);
}
}
}
/**
* Dispose of this theme manager.
*
* @suppress {checkTypes}
* @internal
*/
dispose() {
this.owner_ = null;
// AnyDuringMigration because: Type 'null' is not assignable to type
// 'Theme'.
this.theme = null as AnyDuringMigration;
// AnyDuringMigration because: Type 'null' is not assignable to type
// 'Workspace[]'.
this.subscribedWorkspaces_ = null as AnyDuringMigration;
this.componentDB.clear();
}
}
export namespace ThemeManager {
/** The type for a Blockly UI Component. */
export interface Component {
element: Element;
propertyName: string;
}
}
export type Component = ThemeManager.Component;