stem-core
Version:
Frontend and core-library framework
168 lines (138 loc) • 4.38 kB
JavaScript
import {Dispatchable} from "../../base/Dispatcher";
import {UI} from "../UIBase";
import {StyleSheet} from "../Style";
import {CallThrottler} from "../../base/Utils";
function getInstance(styleSheet) {
if (typeof styleSheet === "function") {
if (typeof styleSheet.getInstance === "function") {
styleSheet = styleSheet.getInstance();
} else {
styleSheet = styleSheet();
}
}
return styleSheet;
}
function getInstanceForObject(obj) {
if (!obj) {
return null;
}
let styleSheet = (obj.theme && obj.theme.get(obj)) || obj.styleSheet;
return getInstance(styleSheet);
}
class Theme extends Dispatchable {
static PropsSymbol = Symbol("Props");
classSet = new Set();
styleSheetSet = new Set();
properties = {};
updateThrottler = new CallThrottler({throttle: 50});
updateThrottled = this.updateThrottler.wrap(() => this.updateStyleSheets());
constructor(name="") {
super();
this.styleSheetSymbol = Symbol("Theme" + name);
window.addEventListener("resize", this.updateThrottled);
}
register(cls, styleSheet) {
cls.theme = this;
cls.styleSheet = styleSheet;
this.set(cls, styleSheet);
}
set(cls, styleSheet) {
cls[this.styleSheetSymbol] = styleSheet;
this.classSet.add(cls);
}
get(cls) {
if (!(typeof cls === "function")) {
cls = cls.constructor;
}
return cls[this.styleSheetSymbol];
}
getProperties() {
return this.properties;
}
getProperty(key, defaultValue) {
const value = this.properties[key] || defaultValue;
return (typeof value === "function") ? value() : value;
}
setProperties(properties, update=true) {
Object.assign(this.properties, properties);
if (update) {
this.updateThrottled();
}
}
setProperty(key, value) {
this.properties[key] = value;
}
getAllStyleSheets() {
let styleSheetSet = new Set(this.styleSheetSet);
for (const cls of this.classSet.values()) {
styleSheetSet.add(this.get(cls));
}
return Array.from(styleSheetSet).map(styleSheet => {
if (styleSheet.getInstance) {
return styleSheet.getInstance();
}
return styleSheet;
});
}
addStyleSheet(styleSheet) {
this.styleSheetSet.add(styleSheet);
}
removeStyleSheet(styleSheet) {
this.styleSheetSet.delete(styleSheet);
}
updateStyleSheets() {
this.dispatch("beforeUpdateStyleSheets");
for (const styleSheet of this.getAllStyleSheets()) {
if (styleSheet.update) {
styleSheet.update();
}
}
this.dispatch("afterUpdateStyleSheets");
}
static register(cls, styleSheet) {
return this.Global.register(...arguments);
}
static get(cls) {
return this.Global.get(...arguments);
}
static addStyleSheet(styleSheet) {
this.Global.addStyleSheet(styleSheet);
}
static setProperties(properties) {
this.Global.setProperties(...arguments);
}
static getProperties() {
return this.Global.getProperties();
}
get props() {
let props = this[this.constructor.PropsSymbol];
if (!props) {
props = this[this.constructor.PropsSymbol] = new Proxy(this.properties, {
get: (properties, key, receiver) => {
// Can default to another theme here.
return properties[key];
},
set: (properties, key, value) => {
this.setProperty(key, value);
this.updateThrottled();
return value;
}
});
}
return props;
}
static get props() {
return this.Global.props;
}
}
Theme.Global = new Theme("Global");
UI.Element.prototype.getStyleSheet = function styleSheetGetter() {
return getInstanceForObject(this.options) || getInstanceForObject(this.constructor);
};
function registerStyle(styleClass, theme=Theme.Global) {
return (target) => {
theme.register(target, styleClass);
}
}
StyleSheet.theme = Theme.Global;
export {Theme, registerStyle};