Create settings builder
This commit is contained in:
parent
53b4bed979
commit
00b5d800a0
|
@ -26,6 +26,7 @@ import SearchInput from "@ui/settings/components/search";
|
||||||
import SliderInput from "@ui/settings/components/slider";
|
import SliderInput from "@ui/settings/components/slider";
|
||||||
import SwitchInput from "@ui/settings/components/switch";
|
import SwitchInput from "@ui/settings/components/switch";
|
||||||
import TextInput from "@ui/settings/components/textbox";
|
import TextInput from "@ui/settings/components/textbox";
|
||||||
|
import SettingGroup from "@ui/settings/group";
|
||||||
|
|
||||||
const bounded = new Map();
|
const bounded = new Map();
|
||||||
const PluginAPI = new AddonAPI(PluginManager);
|
const PluginAPI = new AddonAPI(PluginManager);
|
||||||
|
@ -79,6 +80,7 @@ export default class BdApi {
|
||||||
get SliderInput() {return SliderInput;},
|
get SliderInput() {return SliderInput;},
|
||||||
get SwitchInput() {return SwitchInput;},
|
get SwitchInput() {return SwitchInput;},
|
||||||
get TextInput() {return TextInput;},
|
get TextInput() {return TextInput;},
|
||||||
|
get SettingGroup() {return SettingGroup;}
|
||||||
};
|
};
|
||||||
Net = {fetch};
|
Net = {fetch};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import Modals from "@ui/modals";
|
||||||
import Toasts from "@ui/toasts";
|
import Toasts from "@ui/toasts";
|
||||||
import Notices from "@ui/notices";
|
import Notices from "@ui/notices";
|
||||||
import Tooltip from "@ui/tooltip";
|
import Tooltip from "@ui/tooltip";
|
||||||
|
import Group, {buildSetting} from "@ui/settings/group";
|
||||||
|
import React from "@modules/react";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,6 +139,64 @@ const UI = {
|
||||||
if (data.error) throw new Error(data.error);
|
if (data.error) throw new Error(data.error);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a single setting wrapped in a setting item that has a name and note.
|
||||||
|
* The shape of the object should match the props of the component you want to render, check the
|
||||||
|
* `BdApi.Components` section for details. Shown below are ones common to all setting types.
|
||||||
|
* @param {object} setting
|
||||||
|
* @param {string} setting.id Identifier to used for callbacks
|
||||||
|
* @param {string} setting.name Visual name to display
|
||||||
|
* @param {string} setting.note Visual description to display
|
||||||
|
* @param {any} setting.value Current value of the setting
|
||||||
|
* @param {CallableFunction} [setting.onChange] Callback when the value changes (only argument is new value)
|
||||||
|
* @param {boolean} [setting.disabled] Whether this setting is disabled
|
||||||
|
|
||||||
|
* @returns A SettingItem with a an input as the child
|
||||||
|
*/
|
||||||
|
buildSetting(setting) {
|
||||||
|
return buildSetting(setting);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a settings panel (react element) based on json-like data.
|
||||||
|
*
|
||||||
|
* The `settings` array here is an array of the same settings types described in `buildSetting` above.
|
||||||
|
* However, this API allows one additional setting "type" called `group`. This has the same properties
|
||||||
|
* as the React Component found under the `Components` API.
|
||||||
|
*
|
||||||
|
* `onChange` will always be given 3 arguments: group id, setting id, and setting value. In the case
|
||||||
|
* that you have settings on the "root" of the panel, the group id is `null`.
|
||||||
|
*
|
||||||
|
* `onDrawerToggle` is given 2 arguments: group id, and the current shown state. You can use this to
|
||||||
|
* save drawer states.
|
||||||
|
*
|
||||||
|
* `getDrawerState` is given 2 arguments: group id, and the default shown state. You can use this to
|
||||||
|
* recall a saved drawer state.
|
||||||
|
*
|
||||||
|
* @param {object} props
|
||||||
|
* @param {Array<object>} props.settings Array of settings to show
|
||||||
|
* @param {CallableFunction} props.onChange Function called on every change
|
||||||
|
* @param {CallableFunction} [props.onDrawerToggle] Optionally used to save drawer states
|
||||||
|
* @param {CallableFunction} [props.getDrawerState] Optionially used to recall drawer states
|
||||||
|
* @returns React element usable for a settings panel
|
||||||
|
*/
|
||||||
|
buildSettingsPanel({settings, onChange, onDrawerToggle, getDrawerState}) {
|
||||||
|
if (!settings?.length) throw new Error("No settings provided!");
|
||||||
|
if (typeof(onChange) !== "function") throw new Error("No change listener provided!");
|
||||||
|
return React.createElement(React.Fragment, null, settings.map(setting => {
|
||||||
|
if (setting.type === "group") {
|
||||||
|
const shownByDefault = setting.hasOwnProperty("shown") ? setting.shown : true;
|
||||||
|
const groupProps = Object.assign({}, setting, {
|
||||||
|
onChange,
|
||||||
|
onDrawerToggle: state => onDrawerToggle?.(setting.id, state),
|
||||||
|
shown: getDrawerState?.(setting.id, shownByDefault) ?? shownByDefault
|
||||||
|
});
|
||||||
|
return React.createElement(Group, groupProps);
|
||||||
|
}
|
||||||
|
return buildSetting(Object.assign({}, setting, {onChange: value => onChange(null, setting.id, value)}));
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
import DiscordModules from "./discordmodules";
|
import DiscordModules from "./discordmodules";
|
||||||
export default DiscordModules.React;
|
/** @type {import("react")} */
|
||||||
|
const React = DiscordModules.React;
|
||||||
|
export default React;
|
||||||
export const ReactDOM = DiscordModules.ReactDOM;
|
export const ReactDOM = DiscordModules.ReactDOM;
|
|
@ -1,39 +1,45 @@
|
||||||
import React from "@modules/react";
|
import React from "@modules/react";
|
||||||
|
|
||||||
import Drawer from "./drawer";
|
import Drawer from "./drawer";
|
||||||
import Switch from "./components/switch";
|
import Switch from "./components/switch";
|
||||||
import Dropdown from "./components/dropdown";
|
import Dropdown from "./components/dropdown";
|
||||||
import Number from "./components/number";
|
import Number from "./components/number";
|
||||||
import Item from "./components/item";
|
import Item from "./components/item";
|
||||||
import Textbox from "./components/textbox";
|
import Textbox from "./components/textbox";
|
||||||
import Slider from "./components/slider";
|
import Slider from "./components/slider";
|
||||||
import Radio from "./components/radio";
|
import Radio from "./components/radio";
|
||||||
import Keybind from "./components/keybind";
|
import Keybind from "./components/keybind";
|
||||||
import Color from "./components/color";
|
import Color from "./components/color";
|
||||||
|
|
||||||
const {useCallback} = React;
|
const {useCallback} = React;
|
||||||
|
|
||||||
|
|
||||||
export default function Group({onChange, id, name, button, shown, onDrawerToggle, showDivider, collapsible, settings}) {
|
export default function Group({onChange, id, name, button, shown, onDrawerToggle, showDivider, collapsible, settings, children}) {
|
||||||
const change = useCallback((settingId, value) => {
|
const change = useCallback((settingId, value) => {
|
||||||
if (id) onChange?.(id, settingId, value);
|
if (id) onChange?.(id, settingId, value);
|
||||||
else onChange?.(settingId, value);
|
else onChange?.(settingId, value);
|
||||||
}, [id, onChange]);
|
}, [id, onChange]);
|
||||||
|
|
||||||
return <Drawer collapsible={collapsible} name={name} button={button} shown={shown} onDrawerToggle={onDrawerToggle} showDivider={showDivider}>
|
return <Drawer collapsible={collapsible} name={name} button={button} shown={shown} onDrawerToggle={onDrawerToggle} showDivider={showDivider}>
|
||||||
{settings.filter(s => !s.hidden).map((setting) => {
|
{settings.length && settings.filter(s => !s.hidden).map((setting) => {
|
||||||
let component = null;
|
const callback = value => change(setting.id, value);
|
||||||
const callback = value => change(setting.id, value);
|
return buildSetting({...setting, onChange: callback});
|
||||||
if (setting.type == "dropdown") component = <Dropdown disabled={setting.disabled} id={setting.id} options={setting.options} value={setting.value} onChange={callback} />;
|
})}
|
||||||
if (setting.type == "number") component = <Number disabled={setting.disabled} id={setting.id} min={setting.min} max={setting.max} step={setting.step} value={setting.value} onChange={callback} />;
|
{children}
|
||||||
if (setting.type == "switch") component = <Switch disabled={setting.disabled} id={setting.id} checked={setting.value} onChange={callback} />;
|
</Drawer>;
|
||||||
if (setting.type == "text") component = <Textbox disabled={setting.disabled} id={setting.id} value={setting.value} onChange={callback} />;
|
}
|
||||||
if (setting.type == "slider") component = <Slider disabled={setting.disabled} id={setting.id} min={setting.min} max={setting.max} step={setting.step} value={setting.value} onChange={callback} />;
|
|
||||||
if (setting.type == "radio") component = <Radio disabled={setting.disabled} id={setting.id} name={setting.id} options={setting.options} value={setting.value} onChange={callback} />;
|
|
||||||
if (setting.type == "keybind") component = <Keybind disabled={setting.disabled} id={setting.id} value={setting.value} max={setting.max} onChange={callback} />;
|
export function buildSetting(setting) {
|
||||||
if (setting.type == "color") component = <Color disabled={setting.disabled} id={setting.id} value={setting.value} defaultValue={setting.defaultValue} colors={setting.colors} onChange={callback} />;
|
let component = null;
|
||||||
if (!component) return null;
|
if (setting.type == "dropdown") component = <Dropdown disabled={setting.disabled} id={setting.id} options={setting.options} value={setting.value} onChange={setting.onChange} />;
|
||||||
return <Item id={setting.id} inline={setting.type !== "radio"} key={setting.id} name={setting.name} note={setting.note}>{component}</Item>;
|
if (setting.type == "number") component = <Number disabled={setting.disabled} id={setting.id} min={setting.min} max={setting.max} step={setting.step} value={setting.value} onChange={setting.onChange} />;
|
||||||
})}
|
if (setting.type == "switch") component = <Switch disabled={setting.disabled} id={setting.id} checked={setting.value} onChange={setting.onChange} />;
|
||||||
</Drawer>;
|
if (setting.type == "text") component = <Textbox disabled={setting.disabled} id={setting.id} value={setting.value} onChange={setting.onChange} />;
|
||||||
|
if (setting.type == "slider") component = <Slider disabled={setting.disabled} id={setting.id} min={setting.min} max={setting.max} step={setting.step} value={setting.value} onChange={setting.onChange} />;
|
||||||
|
if (setting.type == "radio") component = <Radio disabled={setting.disabled} id={setting.id} name={setting.id} options={setting.options} value={setting.value} onChange={setting.onChange} />;
|
||||||
|
if (setting.type == "keybind") component = <Keybind disabled={setting.disabled} id={setting.id} value={setting.value} max={setting.max} onChange={setting.onChange} />;
|
||||||
|
if (setting.type == "color") component = <Color disabled={setting.disabled} id={setting.id} value={setting.value} defaultValue={setting.defaultValue} colors={setting.colors} onChange={setting.onChange} />;
|
||||||
|
if (!component) return null;
|
||||||
|
return <Item id={setting.id} inline={setting.type !== "radio"} key={setting.id} name={setting.name} note={setting.note}>{component}</Item>;
|
||||||
}
|
}
|
Loading…
Reference in New Issue