import * as React from "react"; import {useState} from "react"; import {CssEditorEvents, CssEditorUserData, CssVariable} from "tc-shared/ui/modal/css-editor/Definitions"; import {Registry, RegistryMap} from "tc-shared/events"; import {Translatable} from "tc-shared/ui/react-elements/i18n"; import {BoxedInputField, FlatInputField} from "tc-shared/ui/react-elements/InputField"; import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots"; import {Checkbox} from "tc-shared/ui/react-elements/Checkbox"; import {Button} from "tc-shared/ui/react-elements/Button"; import {createErrorModal, createInfoModal} from "tc-shared/ui/elements/Modal"; import {AbstractModal} from "tc-shared/ui/react-elements/ModalDefinitions"; const cssStyle = require("./Renderer.scss"); const CssVariableRenderer = React.memo((props: { events: Registry, variable: CssVariable, selected: boolean }) => { const [selected, setSelected] = useState(props.selected); const [override, setOverride] = useState(props.variable.overwriteValue); const [overrideColor, setOverrideColor] = useState(props.variable.customValue); props.events.reactUse("action_select_entry", event => setSelected(event.variable === props.variable)); props.events.reactUse("action_override_toggle", event => { if (event.variableName !== props.variable.name) return; setOverride(event.enabled); if (event.enabled) setOverrideColor(event.value); }); props.events.reactUse("action_change_override_value", event => { if (event.variableName !== props.variable.name) return; setOverrideColor(event.value); }); return (
{ if (selected) return; props.events.fire("action_select_entry", {variable: props.variable}) }} >
{props.variable.name}
); }); const CssVariableListBodyRenderer = (props: { events: Registry }) => { const [variables, setVariables] = useState<"loading" | CssVariable[]>(() => { props.events.fire_react("query_css_variables"); return "loading"; }); const [filter, setFilter] = useState(undefined); const [selectedVariable, setSelectedVariable] = useState(undefined); props.events.reactUse("action_select_entry", event => setSelectedVariable(event.variable)); props.events.reactUse("query_css_variables", () => setVariables("loading")); let content; if (variables === "loading") { content = (
Loading 
); } else { content = []; for (const variable of variables) { if (filter && variable.name.toLowerCase().indexOf(filter) === -1) continue; content.push(); } if (content.length === 0) { content.push( ); } } props.events.reactUse("action_set_filter", event => setFilter(event.filter?.toLowerCase())); props.events.reactUse("notify_css_variables", event => setVariables(event.variables)); return (
{ if (variables === "loading") return; /* TODO: This isn't working since the div isn't focused properly yet */ let offset = 0; if (event.key === "ArrowDown") { offset = 1; } else if (event.key === "ArrowUp") { offset = -1; } if (offset !== 0) { const selectIndex = variables.findIndex(e => e === selectedVariable); if (selectIndex === -1) return; const variable = variables[selectIndex + offset]; if (!variable) return; props.events.fire("action_select_entry", {variable: variable}); } }} tabIndex={0}> {content}
); }; const CssVariableListSearchRenderer = (props: { events: Registry }) => { const [isLoading, setLoading] = useState(true); props.events.reactUse("notify_css_variables", () => setLoading(false)); props.events.reactUse("query_css_variables", () => setLoading(true)); return (
Filter variables} labelType={"floating"} className={cssStyle.input} onInput={text => props.events.fire("action_set_filter", {filter: text})} disabled={isLoading} />
); }; const CssVariableListRenderer = (props: { events: Registry }) => ( ); const SelectedVariableInfo = (props: { events: Registry }) => { const [selectedVariable, setSelectedVariable] = useState(undefined); props.events.reactUse("action_select_entry", event => setSelectedVariable(event.variable)); return (<>
Name
Default Value
); }; const OverrideVariableInfo = (props: { events: Registry }) => { const [selectedVariable, setSelectedVariable] = useState(undefined); const [overwriteValue, setOverwriteValue] = useState(undefined); const [overwriteEnabled, setOverwriteEnabled] = useState(false); props.events.reactUse("action_select_entry", event => { setSelectedVariable(event.variable); setOverwriteEnabled(event.variable?.overwriteValue); setOverwriteValue(event.variable?.customValue); }); props.events.reactUse("action_override_toggle", event => { if (event.variableName !== selectedVariable?.name) return; selectedVariable.overwriteValue = event.enabled; setOverwriteEnabled(event.enabled); if (event.enabled) { setOverwriteValue(event.value); } }, true, [selectedVariable]); props.events.reactUse("action_change_override_value", event => { if (event.variableName !== selectedVariable?.name) return; setOverwriteValue(event.value); }, true, [selectedVariable]); return (<>
Override Value { props.events.fire("action_override_toggle", { variableName: selectedVariable.name, value: typeof selectedVariable.customValue === "string" ? selectedVariable.customValue : selectedVariable.defaultValue, enabled: value }); }} />
{ selectedVariable.customValue = text; props.events.fire("action_change_override_value", { value: text, variableName: selectedVariable.name }); }} />
); }; const CssVariableColorPicker = (props: { events: Registry, selectedVariable: CssVariable }) => { const [overwriteValue, setOverwriteValue] = useState(undefined); const [overwriteEnabled, setOverwriteEnabled] = useState(false); props.events.reactUse("action_override_toggle", event => { if (event.variableName !== props.selectedVariable?.name) return; props.selectedVariable.overwriteValue = event.enabled; setOverwriteEnabled(event.enabled); if (event.enabled) setOverwriteValue(event.value); }); props.events.reactUse("action_change_override_value", event => { if (event.variableName !== props.selectedVariable?.name || 'cpInvoker' in event) return; setOverwriteValue(event.value); }); let currentInput: string; let inputTimeout: number; return ( ) }; const ControlButtons = (props: { events: Registry }) => { return (
) }; const CssVariableEditor = (props: { events: Registry }) => { return ( ) }; const downloadTextAsFile = (text, name) => { const element = document.createElement("a"); element.text = "download"; element.href = "data:test/plain;charset=utf-8," + encodeURIComponent(text); element.download = name; element.style.display = "none"; document.body.appendChild(element); element.click(); element.remove(); }; const requestFileAsText = async (): Promise => { const element = document.createElement("input"); element.style.display = "none"; element.type = "file"; document.body.appendChild(element); element.click(); await new Promise(resolve => { element.onchange = resolve; }); if (element.files.length !== 1) return undefined; const file = element.files[0]; element.remove(); return await file.text(); }; class PopoutConversationUI extends AbstractModal { private readonly events: Registry; private readonly userData: CssEditorUserData; constructor(registryMap: RegistryMap, userData: CssEditorUserData) { super(); this.userData = userData; this.events = registryMap["default"] as any; this.events.on("notify_export_result", event => { createInfoModal(tr("Config exported successfully"), tr("The config has been exported successfully.")).open(); downloadTextAsFile(event.config, "teaweb-style.json"); }); this.events.on("notify_import_result", event => { if (event.success) createInfoModal(tr("Config imported successfully"), tr("The config has been imported successfully.")).open(); else createErrorModal(tr("Config imported failed"), tr("The config import has been failed.")).open(); }) } renderBody() { return (
); } renderTitle() { return "CSS Variable editor"; } } export = PopoutConversationUI;