import * as React from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { Controls } from "../icons"; import { Modal } from "../ui"; import config from "../get-config"; import queryString from "query-string"; import type { AddonProps } from "../../../shared/types"; import { ActionType, ControlState, ControlType, GlobalAction, GlobalState, } from "../../../shared/types"; const getInputType = (type?: ControlType) => { switch (type) { case ControlType.Boolean: return "checkbox"; case ControlType.Number: return "number"; case ControlType.Range: return "range"; default: return "text"; } }; const getInputValue = (target: HTMLInputElement, type?: ControlType) => { switch (type) { case ControlType.Boolean: return target.checked; case ControlType.Number: case ControlType.Range: return parseFloat(target.value); default: return target.value; } }; const coerceString = (value: string, options?: any[]) => { if (options && options.some((option) => option === Number(value))) { return Number(value); } const isBoolean = value === "true" || value === "false"; return isBoolean ? (value === "false" ? false : true) : value; }; export const getQuery = ( locationSearch: string, controlState: ControlState, ) => { const params = queryString.parse(locationSearch); const controls: { [key: string]: any } = {}; // we first need controls being initialized from stories so we know types // before taking over from url if (Object.keys(controlState).length === 0) { return controlState; } Object.keys(params).forEach((paramKey) => { if (paramKey.startsWith("arg-") && controlState[paramKey.split("-")[1]]) { const keyParts = paramKey.split("-"); const argKey = keyParts[1]; const argValue = params[paramKey] as string; const argType = controlState[argKey].type; if (argType !== ControlType.Action) { let realValue: any = argValue; switch (argType) { case ControlType.String: realValue = decodeURI(argValue); break; case ControlType.Boolean: realValue = argValue === "true"; break; case ControlType.Range: realValue = parseFloat(argValue); break; case ControlType.Number: realValue = parseInt(argValue, 10); break; case ControlType.Complex: realValue = JSON.parse(decodeURI(argValue)); break; case ControlType.Radio: case ControlType.InlineRadio: case ControlType.Select: case ControlType.Background: realValue = coerceString( decodeURI(argValue), controlState[argKey].options, ); break; case ControlType.InlineCheck: case ControlType.MultiSelect: case ControlType.Check: realValue = coerceString( JSON.parse(decodeURI(argValue)), controlState[argKey].options, ); break; } controls[argKey] = { value: realValue, defaultValue: controlState[argKey].defaultValue, description: controlState[argKey].description, type: controlState[argKey].type, }; } } }); return controls; }; const Control = ({ controlKey, globalState, dispatch, }: { controlKey: string; globalState: GlobalState; dispatch: React.Dispatch; }) => { const controlState = globalState.control[controlKey]; const controlName = globalState.control[controlKey].name || controlKey; if (globalState.control[controlKey].type === ControlType.Action) { return ( {controlName} action ); } if (globalState.control[controlKey].type === ControlType.Function) { return ( {controlName} function ); } if ( globalState.control[controlKey].type === ControlType.Radio || globalState.control[controlKey].type === ControlType.InlineRadio || (globalState.control[controlKey].type === ControlType.Background && (globalState.control[controlKey].options as string[]).length < 5) ) { return ( {controlName} {(globalState.control[controlKey].options || []).map((option) => { const value = globalState.control[controlKey].value; const labels = globalState.control[controlKey].labels || {}; const label = labels[option] || option; const isChecked = value === option || value === String(option); return (
{ dispatch({ type: ActionType.UpdateControl, value: { ...globalState.control, [controlKey]: { ...globalState.control[controlKey], value: coerceString( String(option), globalState.control[controlKey].options, ), }, }, }); }} checked={isChecked} />
); })} ); } if ( globalState.control[controlKey].type === ControlType.Check || globalState.control[controlKey].type === ControlType.InlineCheck || globalState.control[controlKey].type === ControlType.MultiSelect ) { return ( {controlName} {(globalState.control[controlKey].options || []).map((option) => { const value = new Set(globalState.control[controlKey].value); const labels = globalState.control[controlKey].labels || {}; const label = labels[option] || option; return (
{ const newValue = String(option); if (value.has(newValue)) { value.delete(newValue); } else { value.add(newValue); } dispatch({ type: ActionType.UpdateControl, value: { ...globalState.control, [controlKey]: { ...globalState.control[controlKey], value: value.size > 0 ? Array.from(value) : undefined, }, }, }); }} />
); })} ); } if ( globalState.control[controlKey].type === ControlType.Select || globalState.control[controlKey].type === ControlType.Background ) { return ( ); } if (globalState.control[controlKey].type === ControlType.Complex) { let stringValue = ""; try { stringValue = JSON.stringify(globalState.control[controlKey].value); } catch (e) { stringValue = "Object/Array argument must be serializable."; } return (