diff --git a/www/src/components/remote-control.jsx b/www/src/components/remote-control.jsx new file mode 100644 index 0000000..d09f686 --- /dev/null +++ b/www/src/components/remote-control.jsx @@ -0,0 +1,180 @@ +import { createEffect, createMemo, mergeProps } from "solid-js"; +import Remote from "../data/remote"; +import Command from "../data/command"; + +function RemoteControl(props) { + props = mergeProps( + { + remote: new Remote(), + onCommand: () => {}, + }, + props + ); + + const BUTTON_SIZE_REGULAR = "regular"; + const BUTTON_SIZE_SMALL = "small"; + + const BUTTON_HEIGHT = 2; + const BUTTON_WIDTH = BUTTON_HEIGHT * 1.4; + const SMALL_BUTTON_HEIGHT = 1.5; + const SMALL_BUTTON_WIDTH = SMALL_BUTTON_HEIGHT * 1.3334; + const TYPES = Command.TYPES; + + const commandMap = createMemo(() => + props.remote + .getCommands() + .reduce( + (map, command) => ({ ...map, [command.getCommandType()]: command }), + {} + ) + ); + + let layout = [ + [TYPES.POWER, null, TYPES.INPUT], + [TYPES.ONE, TYPES.TWO, TYPES.THREE], + [TYPES.FOUR, TYPES.FIVE, TYPES.SIX], + [TYPES.SEVEN, TYPES.EIGHT, TYPES.NINE], + [TYPES.VOLUME_UP, TYPES.ZERO, TYPES.CHANNEL_UP], + [TYPES.VOLUME_DOWN, TYPES.MUTE, TYPES.CHANNEL_DOWN], + [TYPES.MENU, TYPES.HOME, TYPES.SETTINGS], + [TYPES.OPTIONS, TYPES.UP, TYPES.INFO], + [TYPES.LEFT, TYPES.ENTER, TYPES.RIGHT], + [TYPES.RETURN, TYPES.DOWN, TYPES.EXIT], + [TYPES.RED, TYPES.GREEN, TYPES.YELLOW, TYPES.BLUE], + [TYPES.PLAY, TYPES.PAUSE, TYPES.STOP], + [TYPES.REWIND, null, TYPES.FORWARD], + ]; + + function toButtonProps(type) { + let mapping = { + [TYPES.POWER]: () => ({ icon: "bi-power", text: "" }), + [TYPES.INPUT]: () => ({ icon: "bi-box-arrow-in-right", text: "" }), + [TYPES.VOLUME_UP]: () => ({ icon: "bi-volume-up", text: "" }), + [TYPES.VOLUME_DOWN]: () => ({ icon: "bi-volume-down", text: "" }), + [TYPES.MUTE]: () => ({ icon: "bi-volume-mute", text: "" }), + [TYPES.CHANNEL_UP]: () => ({ icon: "bi-chevron-up", text: "" }), + [TYPES.CHANNEL_DOWN]: () => ({ icon: "bi-chevron-down", text: "" }), + [TYPES.HOME]: () => ({ icon: "bi-house-door", text: "" }), + [TYPES.SETTINGS]: () => ({ icon: "bi-gear", text: "" }), + [TYPES.INFO]: () => ({ icon: "bi-info-circle", text: "" }), + [TYPES.UP]: () => ({ icon: "bi-arrow-up", text: "" }), + [TYPES.DOWN]: () => ({ icon: "bi-arrow-down", text: "" }), + [TYPES.LEFT]: () => ({ icon: "bi-arrow-left", text: "" }), + [TYPES.RIGHT]: () => ({ icon: "bi-arrow-right", text: "" }), + [TYPES.ENTER]: () => ({ icon: "", text: "OK" }), + [TYPES.EXIT]: () => ({ icon: "", text: "EXIT" }), + [TYPES.OPTIONS]: () => ({ icon: "bi-list-task", text: "" }), + [TYPES.RETURN]: () => ({ icon: "bi-arrow-return-left", text: "" }), + [TYPES.ONE]: () => ({ icon: "", text: "1" }), + [TYPES.TWO]: () => ({ icon: "", text: "2" }), + [TYPES.THREE]: () => ({ icon: "", text: "3" }), + [TYPES.FOUR]: () => ({ icon: "", text: "4" }), + [TYPES.FIVE]: () => ({ icon: "", text: "5" }), + [TYPES.SIX]: () => ({ icon: "", text: "6" }), + [TYPES.SEVEN]: () => ({ icon: "", text: "7" }), + [TYPES.EIGHT]: () => ({ icon: "", text: "8" }), + [TYPES.NINE]: () => ({ icon: "", text: "9" }), + [TYPES.ZERO]: () => ({ icon: "", text: "0" }), + [TYPES.MENU]: () => ({ icon: "", text: "MENU" }), + [TYPES.PLAY]: () => ({ icon: "bi-play", text: "" }), + [TYPES.PAUSE]: () => ({ icon: "bi-pause", text: "" }), + [TYPES.STOP]: () => ({ icon: "bi-stop", text: "" }), + [TYPES.REWIND]: () => ({ icon: "bi-rewind", text: "" }), + [TYPES.FORWARD]: () => ({ icon: "bi-fast-forward", text: "" }), + [TYPES.RED]: () => ({ + class: "bg-danger", + buttonSize: BUTTON_SIZE_SMALL, + }), + [TYPES.GREEN]: () => ({ + class: "bg-success", + buttonSize: BUTTON_SIZE_SMALL, + }), + [TYPES.YELLOW]: () => ({ + class: "bg-warning", + buttonSize: BUTTON_SIZE_SMALL, + }), + [TYPES.BLUE]: () => ({ + class: "bg-primary", + buttonSize: BUTTON_SIZE_SMALL, + }), + }; + + if (!(type in mapping)) return {}; + let props = mapping[type](); + props.type = type; + props.text = props.text || ""; + return props; + } + + function PlaceholderButton() { + return ( +
+ ); + } + + function RemoteButton(props) { + props = mergeProps( + { + onClick: () => {}, + disabled: false, + class: "", + buttonSize: BUTTON_SIZE_REGULAR, + icon: "", + text: "", + }, + props + ); + + let buttonWidth = BUTTON_WIDTH; + let buttonHeight = BUTTON_HEIGHT; + if (props.buttonSize === BUTTON_SIZE_SMALL) { + buttonWidth = SMALL_BUTTON_WIDTH; + buttonHeight = SMALL_BUTTON_HEIGHT; + } + let buttonFontSize = buttonHeight * 0.4; + let iconSize = buttonHeight * 0.5; + return ( + + ); + } + + return ( +