UNPKG

@ribajs/bs5

Version:

Bootstrap 5 module for Riba.js

307 lines (268 loc) 8.12 kB
import { BasicComponent, TemplateFunction, HttpService, HttpServiceResponse, } from "@ribajs/core"; import { getLocation } from "@ribajs/utils"; import { BaseCache } from "@ribajs/cache"; import { JsxBs5IconProps } from "../../types/jsx/jsx-icon-props.js"; export class Bs5IconComponent extends BasicComponent { public static tagName = "bs5-icon"; public static cache = new BaseCache<Promise<HttpServiceResponse<string>>>(); static get observedAttributes(): (keyof JsxBs5IconProps)[] { return ["size", "width", "height", "src", "color", "direction", "alt"]; } public scope: any = {}; constructor() { super(); } protected getSvg() { return this.querySelector("svg"); } protected async fetchCached(url: string) { let response = Bs5IconComponent.cache.get(url); if (response) { return response; } response = HttpService.get(url); if (response) { Bs5IconComponent.cache.set(url, response); } return response; } protected async fetchIcon(src: string) { let response: HttpServiceResponse<string>; // Append hostname on ssr if ( window?.ssr && !src.startsWith("http") && !src.startsWith("ftp") && !src.startsWith("sftp") ) { let url: URL; if (window.ssr.env.NEST_INTERN_URL) { url = new URL(src, window.ssr.env.NEST_INTERN_URL); } else if (window.ssr.ctx) { url = new URL( src, window.ssr.ctx.protocol + "://" + window.ssr.ctx.hostname, ); } else { throw new Error("Host for SSR not found!"); } response = await this.fetchCached(url.href); } else { response = await this.fetchCached(src); } if (response.status !== 200) { console.error(response.status); return ""; } if (response.headers.get("content-type")?.includes("image/svg+xml")) { return response.body; } console.error( "[bs5-icon] Only SVG's are supported! But content-type is " + response.headers.get("content-type"), ); return ""; } protected getBasename(src: string) { const pathnames = getLocation(src).pathname.split("/"); return pathnames?.[pathnames.length - 1]; } protected getAlternativeText(src: string) { const basename = this.getBasename(src); return basename.split(".")[0]?.replaceAll("_", " "); } protected async onSrcChanged() { let icon = ""; if (!this.scope.src) { this.innerHTML = ""; return; } const alt = this.getAlternativeText(this.scope.src); if (alt) this.setAttribute("alt", alt); const currentSvg = this.getSvg(); const oldSrc = currentSvg ? currentSvg.getAttribute("src") : ""; // Icon already set (maybe on SSR) if (oldSrc === this.scope.src) { return; } try { icon = await this.fetchIcon(this.scope.src); } catch (error) { console.warn( `Error on fetch icon "${this.scope.src}"! Try to switch the protocol...`, error, ); // Try to switch protocol on error if (this.scope.src.startsWith("//")) { this.scope.src = location.protocol + this.scope.src; } const url = new URL(this.scope.src); if (url.protocol === "http:") { url.protocol = "https:"; } else { url.protocol = "http:"; } try { icon = await this.fetchIcon(url.href); } catch (error2) { console.error( `Error on fetch icon "${this.scope.src}"!`, error, error2, ); return; } } if (!icon) { console.error(`Error on fetch icon "${this.scope.src}"!`); return; } this.innerHTML = icon; const newSvg = this.getSvg(); if (newSvg) { newSvg.setAttribute("src", this.scope.src); } } protected removeColor() { this.className = this.className.replace(/(^|\s)color-\S+/g, ""); this.style.color = ""; } protected setColor(color?: string) { if (!color) { return this.removeColor(); } if (color.includes(",")) { const colorArr = color.split(","); if (colorArr.length > 0) { this.className = this.className.replace(/(^|\s)color-\S+/g, ""); for (let i = 0; i < colorArr.length; i++) { const newColor: string = colorArr[i]; if (newColor.startsWith("#") || newColor.startsWith("rgb")) { this.style.color = newColor; } this.classList.add(`color-${newColor}`); } } } else { this.style.color = color; this.className = this.className.replace(/(^|\s)color-\S+/g, ""); this.classList.add(`color-${color}`); } } protected setSize(size: number) { this.style.height = size + "px"; this.style.width = size + "px"; this.className = this.className.replace(/(^|\s)size-\S+/g, ""); this.classList.add(`size-${size}`); } protected setWidth(width: number) { this.style.width = width + "px"; this.className = this.className.replace(/(^|\s)width-\S+/g, ""); this.classList.add(`width-${width}`); } protected setHeight(height: number) { this.style.height = height + "px"; this.className = this.className.replace(/(^|\s)height-\S+/g, ""); this.classList.add(`height-${height}`); } protected setDirection(direction: string) { let classString = `direction-${direction}`; if (direction === "left") { classString += " rotate-270"; } else if ( direction === "left-top" || direction === "left-up" || direction === "top-left" || direction === "up-left" ) { classString += " rotate-315"; } else if (direction === "top" || direction === "up") { classString += " rotate-0"; } else if ( direction === "top-right" || direction === "up-right" || direction === "right-top" || direction === "right-up" ) { classString += " rotate-45"; } else if (direction === "right") { classString += " rotate-90"; } else if ( direction === "right-bottom" || direction === "right-down" || direction === "bottom-right" || direction === "down-right" ) { classString += " rotate-135"; } else if (direction === "bottom" || direction === "down") { classString += " rotate-180"; } else if ( direction === "left-bottom" || direction === "left-down" || direction === "bottom-left" || direction === "down-left" ) { classString += " rotate-225"; } this.className = this.className.replace(/(^|\s)direction-\S+/g, ""); this.className = this.className.replace(/(^|\s)rotate-\S+/g, ""); this.className += " " + classString; } protected async attributeChangedCallback( name: string, oldValue: any, newValue: any, namespace: string | null, ) { // injects the changed attributes to scope super.attributeChangedCallback(name, oldValue, newValue, namespace); if (name === "src") { // if (!newValue) { // console.warn("The src attribute must have a value!", this.scope); // return ""; // } this.onSrcChanged(); } if (name === "color") { this.setColor(newValue); } if (name === "size") { this.setSize(newValue); } if (name === "width") { this.setWidth(newValue); } if (name === "height") { this.setHeight(newValue); } if (name === "direction") { this.setDirection(newValue); } } protected connectedCallback() { super.connectedCallback(); this.setAttribute("aria-hidden", "true"); this.setAttribute("role", "icon"); this.setAttribute("alt", "icon"); this.classList.add("iconset"); this.init(Bs5IconComponent.observedAttributes); // set default values if (!this.scope.direction) { this.scope.direction = "up"; this.attributeChangedCallback( "direction", null, this.scope.direction, null, ); } } protected template(): ReturnType<TemplateFunction> { return null; } }