const h = preact.h; function ButtonViewNetscape({app_state}) { const params = app_state.button_params; console.debug("Rendering netscape params", params); let color_bg = parseHexColor(params.color_bg); let color_hilight_1 = color2Str(lighten(color_bg, 63)); let color_hilight_2 = color2Str(lighten(color_bg, 31)); let color_shadow_1 = color2Str(darken(color_bg, 0xc0 - 0xa)); let color_shadow_2 = color2Str(darken(color_bg, 0xc0 - 0x54)); let ribbon = null; let ribbon_text = null; if(app_state.button_params.banner.style == "corner_bottom_right") { ribbon = h("polygon", {points: "86,8 86,24 81,29 65,29", fill: params.banner.color}); ribbon_text = h("text", { x: params.banner.text.pos[0], y: params.banner.text.pos[1], "font-family": "sans-serif", "dominant-baseline": "middle", "text-anchor": "middle", "transform-origin": "center", "transform":"rotate(-45)", "font-weight": "bold", "font-size": params.banner.text.font_size, fill: params.banner.text.color, "stroke-width": 0}, params.banner.text.content); } else if(app_state.button_params.banner.style == "right") { ribbon = h("rect", {x: 78, y: 2, width: 8, height: 27, fill: app_state.button_params.banner.color}); ribbon_text = h("text", { x: params.banner.text.pos[0], y: params.banner.text.pos[1], "font-family": "sans-serif", "dominant-baseline": "middle", "text-anchor": "middle", "transform-origin": "center", transform: "rotate(-90)", "font-weight": "bold", "font-size": params.banner.text.font_size, fill: params.banner.text.color, "stroke-width": 0}, params.banner.text.content); } return h("svg", SVG_ATTRIBUTES, h("g", {"stroke-width": 0}, h("polygon", {points: "0,0 88,0 87,1 1,1", fill: color_hilight_1}), h("polygon", {points: "1,1 87,1 86,2 2,2", fill: color_hilight_2}), h("polygon", {points: "88,0 88,31 87,30 87,1", fill: color_shadow_1}), h("polygon", {points: "87,1 87,30 86,29 86,2", fill: color_shadow_2}), h("polygon", {points: "88,31 0,31 1,30 87,30", fill: color_shadow_1}), h("polygon", {points: "87,30 1,30 2,29,86,29", fill: color_shadow_2}), h("polygon", {points: "0,0 1,1 1,30 0,31", fill: color_hilight_1}), h("polygon", {points: "1,1 2,2 2,29 1,30", fill: color_hilight_2}), h("rect", {x: 2, y: 2, width: 84, height: 27, fill: params.color_bg}), ribbon), h("image", {href: params.logo_url, x: 3, y: 3, width: 25, height: 25}), h("text", {x: params.text_bottom.pos[0], y: params.text_bottom.pos[1], "font-family": "Kalam", "font-size": params.text_bottom.font_size, fill: params.text_bottom.color, "stroke-width": 0}, params.text_bottom.content), h("text", {x: params.text_top.pos[0], y: params.text_top.pos[1], "font-family": "sans-serif", "font-size": params.text_top.font_size, fill: params.text_top.color, "stroke-width": 0}, params.text_top.content), ribbon_text); } function TextControlView({params, on_change}) { console.debug("Rendering text params", params); function onContentChange(e) { let new_params = structuredClone(params); new_params.content = e.target.value; on_change(new_params); } function onPosXChange(e) { let new_params = structuredClone(params); new_params.pos[0] = parseFloat(e.target.value); on_change(new_params); } function onPosYChange(e) { let new_params = structuredClone(params); new_params.pos[1] = parseFloat(e.target.value); on_change(new_params); } function onFontSizeChange(e) { let new_params = structuredClone(params); new_params.font_size = parseFloat(e.target.value); on_change(new_params); } function onColorChange(e) { let new_params = structuredClone(params); new_params.color = e.target.value; on_change(new_params); } return h(preact.Fragment, {}, h("div", {"class": "ButtonRowLeft"}, h("label", {}, "Content"), h("input", {type: "text", value: params.content, onchange: onContentChange}), h("label", {}, "Color"), h("input", {type: "color", value: params.color, onchange: onColorChange})), h("div", {"class": "ButtonRowLeft"}, h("label", {}, "x"), h("input", {type: "number", value: params.pos[0], onchange: onPosXChange}), h("label", {}, "y"), h("input", {type: "number", value: params.pos[1], onchange: onPosYChange}), h("label", {}, "Font size"), h("input", {type: "number", value: params.font_size, min: 0, max: 88, step: 0.5, onchange: onFontSizeChange}))); } function EditorViewNetscape({app_state, on_state_change}) { const params = app_state.button_params; function onLogoURLChange(e) { let new_state = structuredClone(app_state); new_state.button_params.logo_url = e.target.value; on_state_change(new_state); } function onBGColorChange(e) { let new_state = structuredClone(app_state); new_state.button_params.color_bg = e.target.value; on_state_change(new_state); } function onTextTopChange(new_params) { let new_state = structuredClone(app_state); new_state.button_params.text_top = new_params; on_state_change(new_state); } function onTextBottomChange(new_params) { let new_state = structuredClone(app_state); new_state.button_params.text_bottom = new_params; on_state_change(new_state); } function onRibbonStyleChange(e) { if(app_state.button_params.banner.style == e.target.value) { return; } let new_state = structuredClone(app_state); let banner = new_state.button_params.banner; banner.style = e.target.value; // With the style change, the text also needs adjustments. We // will just set it to the style’s default. if(banner.style == "corner_bottom_right") { banner.text.pos = [65, 46]; } else if(banner.style == "right") { banner.text.pos = [44, 54]; } on_state_change(new_state); } function onRibbonColorChange(e) { let new_state = structuredClone(app_state); new_state.button_params.banner.color = e.target.value; on_state_change(new_state); } function onRibbonTextChange(new_text) { let new_state = structuredClone(app_state); new_state.button_params.banner.text = new_text; on_state_change(new_state); } return h("div", {}, h("div", {"class": "ButtonRowLeft"}, h("label", {}, "Background color"), h("input", {type: "color", value: params.color_bg, onchange: onBGColorChange})), h("div", {"class": "ButtonRowLeft"}, h("label", {}, "Logo URL"), h("input", {type: "url", value: params.logo_url, onchange: onLogoURLChange})), h("div", {"class": "LabeledPanel"}, h("h2", {}, "Main text"), h(TextControlView, {params: params.text_top, on_change: onTextTopChange})), h("div", {"class": "LabeledPanel"}, h("h2", {}, "Flavor text"), h(TextControlView, {params: params.text_bottom, on_change: onTextBottomChange})), h("div", {"class": "LabeledPanel"}, h("h2", {}, "Ribbon"), h("div", {"class": "ButtonRowLeft"}, h("label", {}, "Style"), h("select", {value: params.banner.style, onchange: onRibbonStyleChange}, h("option", {value: "corner_bottom_right"}, "Bottom right"), h("option", {value: "right"}, "Vertical right")), h("label", {}, "Background color"), h("input", {type: "color", value: params.banner.color, onchange: onRibbonColorChange})), h("div", {"class": "LabeledPanel"}, h("h2", {}, "Text"), h(TextControlView, {params: params.banner.text, on_change: onRibbonTextChange})), ), ); } function EditorView({app_state}) { const [state, setState] = preactHooks.useState(app_state); let preview_view = null; let editor_view = null; if(app_state.button_type == "netscape") { preview_view = ButtonViewNetscape; editor_view = EditorViewNetscape; } function onStateChange(new_state) { console.debug("New state is", new_state); setState(new_state); } return h("div", {}, h(preview_view, {app_state: state}), h("select", {}, h("option", {value: "netscape"}, "Netscape"), ), h(editor_view, {app_state: state, on_state_change: onStateChange})); } function App() { return h("div", {}, h(EditorView, {app_state: DEFAULT_APP_STATE})); } preact.render(h(App, {}), document.getElementById("App"));