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