Fix onchange listeners for individual items

This commit is contained in:
Zerebos 2024-12-07 21:27:26 -05:00
parent 23aa6876d1
commit c3951cba25
No known key found for this signature in database
3 changed files with 42 additions and 11 deletions

View File

@ -69,6 +69,22 @@ export default class BdApi {
get UI() {return UI;} get UI() {return UI;}
get ReactUtils() {return ReactUtils;} get ReactUtils() {return ReactUtils;}
get ContextMenu() {return ContextMenuAPI;} get ContextMenu() {return ContextMenuAPI;}
Components = {
get Tooltip() {return DiscordModules.Tooltip;},
get ColorInput() {return ColorInput;},
get DropdownInput() {return DropdownInput;},
get SettingItem() {return SettingItem;},
get KeybindInput() {return KeybindInput;},
get NumberInput() {return NumberInput;},
get RadioInput() {return RadioInput;},
get SearchInput() {return SearchInput;},
get SliderInput() {return SliderInput;},
get SwitchInput() {return SwitchInput;},
get TextInput() {return TextInput;},
get SettingGroup() {return SettingGroup;},
get ErrorBoundary() {return ErrorBoundary;},
};
Net = {fetch};
} }
// Add legacy functions // Add legacy functions

View File

@ -6,6 +6,7 @@ import Notices from "@ui/notices";
import Tooltip from "@ui/tooltip"; import Tooltip from "@ui/tooltip";
import Group, {buildSetting} from "@ui/settings/group"; import Group, {buildSetting} from "@ui/settings/group";
import React from "@modules/react"; import React from "@modules/react";
import ErrorBoundary from "@ui/errorboundary";
/** /**
@ -80,7 +81,7 @@ const UI = {
* @param {string} [options.video] Youtube link or url of a video file to use as the banner * @param {string} [options.video] Youtube link or url of a video file to use as the banner
* @param {string} [options.poster] URL to use for the video freeze-frame poster * @param {string} [options.poster] URL to use for the video freeze-frame poster
* @param {string|ReactElement|Array<string|ReactElement>} [options.footer] What to show in the modal footer * @param {string|ReactElement|Array<string|ReactElement>} [options.footer] What to show in the modal footer
* @param {Array<object>} [options.changes] List of changes to show (see description for details) * @param {Array<Changes>} [options.changes] List of changes to show (see description for details)
* @returns {string} The key used for this modal. * @returns {string} The key used for this modal.
*/ */
showChangelogModal(options) { showChangelogModal(options) {
@ -169,7 +170,8 @@ const UI = {
* as the Group React Component found under the `Components` API. * as the Group React Component found under the `Components` API.
* *
* `onChange` will always be given 3 arguments: category id, setting id, and setting value. In the case * `onChange` will always be given 3 arguments: category id, setting id, and setting value. In the case
* that you have settings on the "root" of the panel, the category id is `null`. * that you have settings on the "root" of the panel, the category id is `null`. Any `onChange`
* listeners attached to individual settings will fire before the panel-level change listener.
* *
* `onDrawerToggle` is given 2 arguments: category id, and the current shown state. You can use this to * `onDrawerToggle` is given 2 arguments: category id, and the current shown state. You can use this to
* save drawer states. * save drawer states.
@ -186,18 +188,28 @@ const UI = {
*/ */
buildSettingsPanel({settings, onChange, onDrawerToggle, getDrawerState}) { buildSettingsPanel({settings, onChange, onDrawerToggle, getDrawerState}) {
if (!settings?.length) throw new Error("No settings provided!"); 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 => { return React.createElement(ErrorBoundary, null, settings.map(setting => {
if (!setting.id || !setting.type) throw new Error(`Setting item missing id or type`);
if (setting.type === "category") { if (setting.type === "category") {
const shownByDefault = setting.hasOwnProperty("shown") ? setting.shown : true; const shownByDefault = setting.hasOwnProperty("shown") ? setting.shown : true;
const categoryProps = Object.assign({}, setting, {
onChange, return React.createElement(Group, {
...setting,
onChange: onChange,
onDrawerToggle: state => onDrawerToggle?.(setting.id, state), onDrawerToggle: state => onDrawerToggle?.(setting.id, state),
shown: getDrawerState?.(setting.id, shownByDefault) ?? shownByDefault shown: getDrawerState?.(setting.id, shownByDefault) ?? shownByDefault
}); });
return React.createElement(Group, categoryProps);
} }
return buildSetting(Object.assign({}, setting, {onChange: value => onChange(null, setting.id, value)}));
return buildSetting({
...setting,
onChange: value => {
setting?.onChange?.(value);
onChange(null, setting.id, value);
}
});
})); }));
} }

View File

@ -21,8 +21,11 @@ export default function Group({onChange, id, name, button, shown, onDrawerToggle
}, [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.length && settings.filter(s => !s.hidden).map((setting) => { {settings?.length > 0 && settings.filter(s => !s.hidden).map((setting) => {
const callback = value => change(setting.id, value); const callback = value => {
setting?.onChange?.(value);
change(setting.id, value);
};
return buildSetting({...setting, onChange: callback}); return buildSetting({...setting, onChange: callback});
})} })}
{children} {children}
@ -42,5 +45,5 @@ export function buildSetting(setting) {
if (setting.type == "color") children = <Color disabled={setting.disabled} id={setting.id} value={setting.value} defaultValue={setting.defaultValue} colors={setting.colors} onChange={setting.onChange} />; if (setting.type == "color") children = <Color disabled={setting.disabled} id={setting.id} value={setting.value} defaultValue={setting.defaultValue} colors={setting.colors} onChange={setting.onChange} />;
if (setting.type == "custom") children = setting.children; if (setting.type == "custom") children = setting.children;
if (!children) return null; if (!children) return null;
return <Item id={setting.id} inline={setting.hasOwnProperty("inline") ? setting.inline : setting.type !== "radio"} key={setting.id} name={setting.name} note={setting.note}>{children}</Item>; return <Item id={setting.id} inline={setting.hasOwnProperty("inline") ? setting.inline : setting.type !== "radio" && setting.type !== "color"} key={setting.id} name={setting.name} note={setting.note}>{children}</Item>;
} }