/** * JSDoc types */ /** * @typedef {"button" | "toggle" | "checkbox" | "slider" | "color-temp" | "color-light" | "color-wheel" | "thermostat" | "scrubber"} DescType */ /** * @typedef OSmButtonProps * @property {string} [text] * @property {*} [tv] * @property {*} [fv] */ /** * @typedef OSmToggleProps * @property {string} [text] * @property {*} [tv] * @property {*} [fv] */ /** * @typedef OSmSliderProps * @property {number} min * @property {number} max * @property {number} [step] * @property {number} [precision] * @property {boolean} [percent] */ /** * @typedef OSmThermostatProps * @property {number} gague * @property {number} [deviation] * @property {Color} [color_cold] * @property {Color} [color_temperate] * @property {Color} [color_warm] * @property {number} [ct] * @property {number} [tw] */ /** * @typedef OSmScrubberProps * @property {number} max * @property {number} min * @property {number} step * @property {number} [zones] * @property {number} [speed] * @property {number} [spring] */ /** * @typedef {OSmButtonProps | OSmToggleProps | OSmSliderProps | OSmThermostatProps | OSmScrubberProps} OSmAnyProps */ /** * @typedef {Object} DescBase * @property {DescType} type * @property {string} name */ /** * @typedef {DescBase & {type: "button", props: OSmButtonProps, state: never}} DescButton */ /** * @typedef {DescBase & {type: "toggle" | "checkbox", props: OSmToggleProps, state: boolean}} DescToggle */ /** * @typedef {DescBase & {type: "slider", props: OSmSliderProps, state: number}} DescSlider */ /** * @typedef {DescBase & {type: "color-temp", props: never, state: number}} DescColorTemp */ /** * @typedef {DescBase & {type: "color-light", props: never, state: number}} DescColorLight */ /** * @typedef {DescBase & {type: "color-wheel", props: never, state: Color}} DescColorWheel */ /** * @typedef {DescBase & {type: "thermostat", props: OSmThermostatProps, state: number}} DescThermostat */ /** * @typedef {DescBase & {type: "scrubber", props: OSmScrubberProps, state: number}} DescScrubber */ /** * @typedef {DescButton | DescToggle | DescSlider | DescColorTemp | DescColorLight | DescColorWheel | DescThermostat | DescScrubber} DescAny */ class WidgetSet extends EventTarget{ #name = ""; /** @type {{[key: string]: Widget}} */ widgets = {}; /** * * @param {DescAny[]} desc * @param {string} name */ constructor(desc, name) { super(); this.#name = name; this.#parse_desc(desc); for (let i in this.widgets) { this.widgets[i].addEventListener("change", this.#change.bind(this)); } } /** * @param {DescButton} desc * @returns {WidgetButton} */ static parse_button(desc) { let merge = {...{tv: true, fv: false, text: ""}, ...desc.props}; let out = new WidgetButton(merge.tv, merge.fv); out.setId("name", desc.name); out.element.innerText = merge.text; return out; } /** * @param {DescToggle} desc * @returns {WidgetToggle} */ static parse_toggle(desc) { let merge = {...{tv: true, fv: false, text: ""}, ...desc.props}; let out; if (desc.type == "checkbox") out = new WidgetCheckbox(desc.state ? merge.tv : merge.fv, merge.tv, merge.fv); else { out = new WidgetToggle(desc.state ? merge.tv : merge.fv, merge.tv, merge.fv); out.element.innerText = merge.text; } out.setId("name", desc.name); return out; } /** * @param {DescSlider} desc * @returns {WidgetSlider} */ static parse_slider(desc) { let merge = {...{step: 0.1, precision: 1, percent: false}, ...desc.props}; let out = new WidgetSlider(desc.state, merge.min, merge.max, merge.step, merge.precision, merge.percent); out.setId("name", desc.name); return out; } /** * @param {DescColorLight} desc * @returns {WidgetColorLight} */ static parse_color_light(desc) { let out = new WidgetColorLight(Math.max(Math.min(desc.state, 1), 0)); out.setId("name", desc.name); return out; } /** * @param {DescColorTemp} desc * @returns {WidgetColorTemp} */ static parse_color_temp(desc) { let out = new WidgetColorTemp(Math.max(Math.min(desc.state, 6000), 2700)); out.setId("name", desc.name); return out; } /** * @param {DescColorWheel} desc * @returns {WidgetColorWheel} */ static parse_color_wheel(desc) { let value = new Color(...desc.state.channels); let out = new WidgetColorWheel(value); out.setId("name", desc.name); return out; } /** * @param {DescThermostat} desc * @returns {WidgetThermostat} */ static parse_thermostat(desc) { let merge = {...{ deviation: 7, color_cold: Color.from_rgb(0, 133, 255), color_temperate: Color.from_rgb(18, 229, 82), color_warm: Color.from_rgb(255, 149, 0), ct: -2, tw: 2, }, ...desc.props}; let cc = new Color(...merge.color_cold.channels); let ct = new Color(...merge.color_temperate.channels); let cw = new Color(...merge.color_warm.channels); let out = new WidgetThermostat(desc.state, merge.gague, merge.deviation, cc, ct, cw, merge.ct, merge.tw); out.setId("name", desc.name); return out; } /** * @param {DescScrubber} desc * @returns {WidgetScrubber} */ static parse_scrubber(desc) { let merge = {...{ zones: 2, speed: 300, spring: 1.4 }, ...desc.props}; let out = new WidgetScrubber(desc.state, merge.max, merge.min, merge.step, merge.zones, merge.speed, merge.spring); out.setId("name", desc.name); return out; } /** * * @param {DescAny[]} desc */ #parse_desc (desc) { for (let i of desc) { let out; switch (i.type) { case "button": out = WidgetSet.parse_button(i); break; case "checkbox": case "toggle": out = WidgetSet.parse_toggle(i); break; case "slider": out = WidgetSet.parse_slider(i); break; case "color-light": out = WidgetSet.parse_color_light(i); break; case "color-temp": out = WidgetSet.parse_color_temp(i); break; case "color-wheel": out = WidgetSet.parse_color_wheel(i); break; case "thermostat": out = WidgetSet.parse_thermostat(i); break; case "scrubber": out = WidgetSet.parse_scrubber(i); break; } this.widgets[i.name] = out; } } /** * * @param {CustomEvent} e */ #change(e) { /** * @type {{widget: Widget, changed: string}} */ let d = e.detail; let detail = { set: this.#name, name: d.widget.get("name"), changed: d.changed, value: d.widget.get(d.changed) }; this.dispatchEvent(new CustomEvent("change", {detail: detail})); } /** * * @param {HTMLElement} el */ apply_to(el) { for (let i in this.widgets) { el.appendChild(this.widgets[i].element); } } }