this repo has no description
1import {
2 ExtensionSettingType,
3 ExtensionSettingsManifest,
4 NumberSettingType,
5 SelectSettingType
6} from "@moonlight-mod/types/config";
7import WebpackRequire from "@moonlight-mod/types/discord/require";
8import { MoonbaseExtension } from "../types";
9
10type SettingsProps = {
11 ext: MoonbaseExtension;
12 name: string;
13 setting: ExtensionSettingsManifest;
14};
15
16type SettingsComponent = React.ComponentType<SettingsProps>;
17
18export default (require: typeof WebpackRequire) => {
19 const React = require("common_react");
20 const CommonComponents = require("common_components");
21 const Flux = require("common_flux");
22
23 const { MoonbaseSettingsStore } =
24 require("moonbase_stores") as typeof import("../webpackModules/stores");
25
26 function Boolean({ ext, name, setting }: SettingsProps) {
27 const { FormSwitch } = CommonComponents;
28 const { value, displayName } = Flux.useStateFromStores(
29 [MoonbaseSettingsStore],
30 () => {
31 return {
32 value: MoonbaseSettingsStore.getExtensionConfig<boolean>(
33 ext.id,
34 name
35 ),
36 displayName: MoonbaseSettingsStore.getExtensionConfigName(
37 ext.id,
38 name
39 )
40 };
41 },
42 [ext.id, name]
43 );
44
45 return (
46 <FormSwitch
47 value={value ?? false}
48 hideBorder={true}
49 onChange={(value: boolean) => {
50 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
51 }}
52 >
53 {displayName}
54 </FormSwitch>
55 );
56 }
57
58 function Number({ ext, name, setting }: SettingsProps) {
59 const { Slider, ControlClasses } = CommonComponents;
60 const { value, displayName } = Flux.useStateFromStores(
61 [MoonbaseSettingsStore],
62 () => {
63 return {
64 value: MoonbaseSettingsStore.getExtensionConfig<number>(ext.id, name),
65 displayName: MoonbaseSettingsStore.getExtensionConfigName(
66 ext.id,
67 name
68 )
69 };
70 },
71 [ext.id, name]
72 );
73
74 const castedSetting = setting as NumberSettingType;
75 const min = castedSetting.min ?? 0;
76 const max = castedSetting.max ?? 100;
77
78 return (
79 <div>
80 <label className={ControlClasses.title}>{displayName}</label>
81 <Slider
82 initialValue={value ?? 0}
83 minValue={castedSetting.min ?? 0}
84 maxValue={castedSetting.max ?? 100}
85 onValueChange={(value: number) => {
86 const rounded = Math.max(min, Math.min(max, Math.round(value)));
87 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded);
88 }}
89 />
90 </div>
91 );
92 }
93
94 function String({ ext, name, setting }: SettingsProps) {
95 const { TextInput, ControlClasses } = CommonComponents;
96 const { value, displayName } = Flux.useStateFromStores(
97 [MoonbaseSettingsStore],
98 () => {
99 return {
100 value: MoonbaseSettingsStore.getExtensionConfig<string>(ext.id, name),
101 displayName: MoonbaseSettingsStore.getExtensionConfigName(
102 ext.id,
103 name
104 )
105 };
106 },
107 [ext.id, name]
108 );
109
110 return (
111 <div>
112 <label className={ControlClasses.title}>{displayName}</label>
113 <TextInput
114 value={value ?? ""}
115 onChange={(value: string) => {
116 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
117 }}
118 />
119 </div>
120 );
121 }
122
123 function Select({ ext, name, setting }: SettingsProps) {
124 const { ControlClasses, SingleSelect } = CommonComponents;
125 const { value, displayName } = Flux.useStateFromStores(
126 [MoonbaseSettingsStore],
127 () => {
128 return {
129 value: MoonbaseSettingsStore.getExtensionConfig<string>(ext.id, name),
130 displayName: MoonbaseSettingsStore.getExtensionConfigName(
131 ext.id,
132 name
133 )
134 };
135 },
136 [ext.id, name]
137 );
138
139 const castedSetting = setting as SelectSettingType;
140 const options = castedSetting.options;
141
142 return (
143 <div>
144 <label className={ControlClasses.title}>{displayName}</label>
145 <SingleSelect
146 autofocus={false}
147 clearable={false}
148 value={value ?? ""}
149 options={options.map((o) => ({ value: o, label: o }))}
150 onChange={(value: string) => {
151 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
152 }}
153 />
154 </div>
155 );
156 }
157
158 function List({ ext, name, setting }: SettingsProps) {
159 const { ControlClasses, Select, useVariableSelect, multiSelect } =
160 CommonComponents;
161 const { value, displayName } = Flux.useStateFromStores(
162 [MoonbaseSettingsStore],
163 () => {
164 return {
165 value:
166 MoonbaseSettingsStore.getExtensionConfig<string>(ext.id, name) ??
167 [],
168 displayName: MoonbaseSettingsStore.getExtensionConfigName(
169 ext.id,
170 name
171 )
172 };
173 },
174 [ext.id, name]
175 );
176
177 const castedSetting = setting as SelectSettingType;
178 const options = castedSetting.options;
179
180 return (
181 <div>
182 <label className={ControlClasses.title}>{displayName}</label>
183 <Select
184 autofocus={false}
185 clearable={false}
186 options={options.map((o) => ({ value: o, label: o }))}
187 {...useVariableSelect({
188 onSelectInteraction: multiSelect,
189 value: new Set(Array.isArray(value) ? value : [value]),
190 onChange: (value: string) => {
191 MoonbaseSettingsStore.setExtensionConfig(
192 ext.id,
193 name,
194 Array.from(value)
195 );
196 }
197 })}
198 />
199 </div>
200 );
201 }
202
203 function Dictionary({ ext, name, setting }: SettingsProps) {
204 const { TextInput, ControlClasses, Button, Flex } = CommonComponents;
205 const { value, displayName } = Flux.useStateFromStores(
206 [MoonbaseSettingsStore],
207 () => {
208 return {
209 value: MoonbaseSettingsStore.getExtensionConfig<
210 Record<string, string>
211 >(ext.id, name),
212 displayName: MoonbaseSettingsStore.getExtensionConfigName(
213 ext.id,
214 name
215 )
216 };
217 },
218 [ext.id, name]
219 );
220
221 const entries = Object.entries(value ?? {});
222
223 return (
224 <Flex direction={Flex.Direction.VERTICAL}>
225 <label className={ControlClasses.title}>{displayName}</label>
226 {entries.map(([key, val], i) => (
227 // FIXME: stylesheets
228 <div
229 key={i}
230 style={{
231 display: "grid",
232 height: "40px",
233 gap: "10px",
234 gridTemplateColumns: "1fr 1fr 40px"
235 }}
236 >
237 <TextInput
238 value={key}
239 onChange={(newKey: string) => {
240 entries[i][0] = newKey;
241 MoonbaseSettingsStore.setExtensionConfig(
242 ext.id,
243 name,
244 Object.fromEntries(entries)
245 );
246 }}
247 />
248 <TextInput
249 value={val}
250 onChange={(newValue: string) => {
251 entries[i][1] = newValue;
252 MoonbaseSettingsStore.setExtensionConfig(
253 ext.id,
254 name,
255 Object.fromEntries(entries)
256 );
257 }}
258 />
259 <Button
260 color={Button.Colors.RED}
261 size={Button.Sizes.ICON}
262 onClick={() => {
263 entries.splice(i, 1);
264 MoonbaseSettingsStore.setExtensionConfig(
265 ext.id,
266 name,
267 Object.fromEntries(entries)
268 );
269 }}
270 >
271 X
272 </Button>
273 </div>
274 ))}
275
276 <Button
277 look={Button.Looks.FILLED}
278 color={Button.Colors.GREEN}
279 onClick={() => {
280 entries.push([`entry-${entries.length}`, ""]);
281 MoonbaseSettingsStore.setExtensionConfig(
282 ext.id,
283 name,
284 Object.fromEntries(entries)
285 );
286 }}
287 >
288 Add new entry
289 </Button>
290 </Flex>
291 );
292 }
293
294 function Setting({ ext, name, setting }: SettingsProps) {
295 const elements: Partial<Record<ExtensionSettingType, SettingsComponent>> = {
296 [ExtensionSettingType.Boolean]: Boolean,
297 [ExtensionSettingType.Number]: Number,
298 [ExtensionSettingType.String]: String,
299 [ExtensionSettingType.Select]: Select,
300 [ExtensionSettingType.List]: List,
301 [ExtensionSettingType.Dictionary]: Dictionary
302 };
303 const element = elements[setting.type];
304 if (element == null) return <></>;
305 return React.createElement(element, { ext, name, setting });
306 }
307
308 function Settings({ ext }: { ext: MoonbaseExtension }) {
309 const { Flex } = CommonComponents;
310 return (
311 <Flex direction={Flex.Direction.VERTICAL}>
312 {Object.entries(ext.manifest.settings!).map(([name, setting]) => (
313 <Setting ext={ext} key={name} name={name} setting={setting} />
314 ))}
315 </Flex>
316 );
317 }
318
319 return {
320 Boolean,
321 Settings
322 };
323};