@ribajs/bs5
Version:
Bootstrap 5 module for Riba.js
125 lines (109 loc) • 3.24 kB
text/typescript
import { Component, TemplateFunction } from "@ribajs/core";
import { ThemeService } from "../../services/theme.js";
import { hasChildNodesTrim } from "@ribajs/utils";
import template from "./bs5-theme-button.component.html?raw";
import iconSunUrl from "@ribajs/iconset/svg/icon_sun.svg?url";
import iconMoonUrl from "@ribajs/iconset/svg/icon_moon.svg?url";
import { themeChoices } from "../../constants/index.js";
import type {
Bs5ThemeButtonComponentScope,
ThemeChoice,
ThemeChangedData,
JsxBs5ThemeButtonProps,
} from "../../types/index.js";
/**
* @see https://github.com/TypeStrong/typedoc/blob/master/src/lib/output/themes/default/assets/typedoc/Theme.ts
*/
export class Bs5ThemeButtonComponent extends Component {
public static tagName = "bs5-theme-button";
protected theme: ThemeService;
static get observedAttributes(): (keyof JsxBs5ThemeButtonProps)[] {
return ["mode", "labels", "light-icon-src", "icon-size", "dark-icon-src"];
}
public scope: Bs5ThemeButtonComponentScope = {
// Options // Attributes
mode: "dropdown",
labels: {
os: "OS",
light: "Light",
dark: "Dark",
},
lightIconSrc: iconSunUrl,
darkIconSrc: iconMoonUrl,
iconSize: 32,
// Methods / Properties
setTheme: this.setTheme.bind(this),
selectTheme: this.selectTheme.bind(this),
toggleTheme: this.toggleTheme.bind(this),
selected: undefined,
choices: themeChoices,
};
constructor() {
super();
this.theme = ThemeService.getSingleton();
}
protected connectedCallback() {
super.connectedCallback();
this.init(Bs5ThemeButtonComponent.observedAttributes);
this.addEventListeners();
this.initTheme();
}
protected addEventListeners() {
this.theme.onChange((data) => {
this.onChange(data);
});
}
protected onChange(data: ThemeChangedData) {
this.scope.selected = data.newValue.choice;
}
protected async beforeBind() {
this.initTheme();
}
initTheme() {
const selectEl = this.getElementsByTagName("select")?.item(0);
const data = this.theme.init();
this.scope.selected = data.choice || undefined;
if (selectEl) this.theme.select(this.scope.selected, selectEl);
}
public setTheme(theme: ThemeChoice) {
this.theme.set(theme);
}
/**
* Used in `dropdown` mode (which uses a select element)
*/
public selectTheme() {
if (this.scope.selected) this.theme.set(this.scope.selected);
}
/**
* Used in `icon` mode (which uses a button element)
*/
public toggleTheme() {
const current = this.theme.getScheme();
if (current.bySystem) {
if (current.systemIsLight) {
this.theme.set("dark");
} else {
this.theme.set("light");
}
} else if (current.isDark) {
if (current.supported && current.systemIsLight) {
this.theme.set("os");
} else {
this.theme.set("light");
}
} else {
if (current.supported && current.systemIsDark) {
this.theme.set("os");
} else {
this.theme.set("dark");
}
}
}
protected template(): ReturnType<TemplateFunction> {
if (!hasChildNodesTrim(this)) {
return template;
} else {
return null;
}
}
}