import { Color, ComponentId, AnyFill } from "../dataModels"; import { colorToCssString, fillToCss } from "../cssUtils"; import { ComponentBase, ComponentState } from "./componentBase"; import { RippleEffect } from "../rippleEffect"; export type RectangleState = ComponentState & { _type_: "Rectangle-builtin"; content?: ComponentId | null; transition_time?: number; cursor?: string; ripple?: boolean; fill?: AnyFill; stroke_color?: Color; stroke_width?: number; corner_radius?: [number, number, number, number]; shadow_color?: Color; shadow_radius?: number; shadow_offset_x?: number; shadow_offset_y?: number; hover_fill?: AnyFill | null; hover_stroke_color?: Color | null; hover_stroke_width?: number | null; hover_corner_radius?: [number, number, number, number] | null; hover_shadow_color?: Color | null; hover_shadow_radius?: number | null; hover_shadow_offset_x?: number | null; hover_shadow_offset_y?: number | null; }; function numberToRem(num: number): string { return `${num}rem`; } // Functions that convert an attribute of `RectangleState` to CSS. The return // value can either be a string or an object of `{cssProperty: cssValue}`. const JS_TO_CSS_VALUE: { [attr: string]: (value: any) => string | { [attr: string]: string }; } = { fill: fillToCss, stroke_color: colorToCssString, stroke_width: numberToRem, corner_radius: (radii: [number, number, number, number]) => radii.map((num) => `${num}rem`).join(" "), shadow_color: colorToCssString, shadow_radius: numberToRem, shadow_offset_x: numberToRem, shadow_offset_y: numberToRem, }; function cursorToCSS(cursor: string): string { const CURSOR_MAP = { default: "auto", none: "none", help: "help", pointer: "pointer", loading: "wait", backgroundLoading: "progress", crosshair: "crosshair", text: "text", move: "move", notAllowed: "not-allowed", canGrab: "grab", isGrabbed: "grabbing", zoomIn: "zoom-in", zoomOut: "zoom-out", }; console.assert(cursor in CURSOR_MAP, `Unknown cursor: ${cursor}`); return CURSOR_MAP[cursor]; } export class RectangleComponent extends ComponentBase { state: Required; // If this rectangle has a ripple effect, this is the ripple instance. // `null` otherwise. private rippleInstance: RippleEffect | null = null; createElement(): HTMLElement { let element = document.createElement("div"); element.classList.add("rio-rectangle"); return element; } updateElement( deltaState: RectangleState, latentComponents: Set ): void { super.updateElement(deltaState, latentComponents); this.replaceOnlyChild(latentComponents, deltaState.content); if (deltaState.transition_time !== undefined) { this.element.style.transitionDuration = `${deltaState.transition_time}s`; } if (deltaState.cursor !== undefined) { this.element.style.cursor = cursorToCSS(deltaState.cursor); } if (deltaState.ripple === true) { if (this.rippleInstance === null) { this.rippleInstance = new RippleEffect(this.element); } } else if (deltaState.ripple === false) { if (this.rippleInstance !== null) { this.rippleInstance.destroy(); this.rippleInstance = null; } } // Apply all the styling properties for (let [attrName, js_to_css] of Object.entries(JS_TO_CSS_VALUE)) { let value = deltaState[attrName]; if (value !== undefined && value !== null) { let cssValues = js_to_css(value); if (typeof cssValues === "string") { cssValues = { [attrName]: cssValues }; } for (let [prop, val] of Object.entries(cssValues)) { this.element.style.setProperty( `--rio-rectangle-${prop}`, val ); } } let hoverValue = deltaState["hover_" + attrName]; if (hoverValue !== undefined) { if (hoverValue === null) { // No hover value? Use the corresponding non-hover value if (value !== undefined && value !== null) { let cssValues = js_to_css(value); if (typeof cssValues === "string") { cssValues = { [attrName]: cssValues }; } for (let [prop, val] of Object.entries(cssValues)) { this.element.style.setProperty( `--rio-rectangle-hover-${prop}`, `var(--rio-rectangle-${prop})` ); } } } else { let cssValues = js_to_css(hoverValue); if (typeof cssValues === "string") { cssValues = { [attrName]: cssValues }; } for (let [prop, val] of Object.entries(cssValues)) { this.element.style.setProperty( `--rio-rectangle-hover-${prop}`, val ); } } } } } }