this repo has no description
at v1.0.5 12 kB view raw
1import { 2 ExtensionSettingType, 3 ExtensionSettingsManifest, 4 MultiSelectSettingType, 5 NumberSettingType, 6 SelectOption, 7 SelectSettingType 8} from "@moonlight-mod/types/config"; 9import WebpackRequire from "@moonlight-mod/types/discord/require"; 10import { CircleXIconSVG, ExtensionState, MoonbaseExtension } from "../../types"; 11 12type SettingsProps = { 13 ext: MoonbaseExtension; 14 name: string; 15 setting: ExtensionSettingsManifest; 16 disabled: boolean; 17}; 18 19type SettingsComponent = React.ComponentType<SettingsProps>; 20 21export default (require: typeof WebpackRequire) => { 22 const React = require("common_react"); 23 const CommonComponents = require("common_components"); 24 const Flux = require("common_flux"); 25 const spacepack = require("spacepack_spacepack").spacepack; 26 27 const { MoonbaseSettingsStore } = 28 require("moonbase_stores") as typeof import("../../webpackModules/stores"); 29 30 const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; 31 32 function useConfigEntry<T>(id: string, name: string) { 33 return Flux.useStateFromStores( 34 [MoonbaseSettingsStore], 35 () => { 36 return { 37 value: MoonbaseSettingsStore.getExtensionConfig<T>(id, name), 38 displayName: MoonbaseSettingsStore.getExtensionConfigName(id, name), 39 description: MoonbaseSettingsStore.getExtensionConfigDescription( 40 id, 41 name 42 ) 43 }; 44 }, 45 [id, name] 46 ); 47 } 48 49 function Boolean({ ext, name, setting, disabled }: SettingsProps) { 50 const { FormSwitch } = CommonComponents; 51 const { value, displayName, description } = useConfigEntry<boolean>( 52 ext.id, 53 name 54 ); 55 56 return ( 57 <FormSwitch 58 value={value ?? false} 59 hideBorder={true} 60 disabled={disabled} 61 onChange={(value: boolean) => { 62 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value); 63 }} 64 note={description} 65 className={`${Margins.marginReset} ${Margins.marginTop20}`} 66 > 67 {displayName} 68 </FormSwitch> 69 ); 70 } 71 72 function Number({ ext, name, setting, disabled }: SettingsProps) { 73 const { FormItem, FormText, Slider } = CommonComponents; 74 const { value, displayName, description } = useConfigEntry<number>( 75 ext.id, 76 name 77 ); 78 79 const castedSetting = setting as NumberSettingType; 80 const min = castedSetting.min ?? 0; 81 const max = castedSetting.max ?? 100; 82 83 return ( 84 <FormItem className={Margins.marginTop20} title={displayName}> 85 {description && <FormText>{description}</FormText>} 86 <Slider 87 initialValue={value ?? 0} 88 disabled={disabled} 89 minValue={castedSetting.min ?? 0} 90 maxValue={castedSetting.max ?? 100} 91 onValueChange={(value: number) => { 92 const rounded = Math.max(min, Math.min(max, Math.round(value))); 93 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded); 94 }} 95 /> 96 </FormItem> 97 ); 98 } 99 100 function String({ ext, name, setting, disabled }: SettingsProps) { 101 const { FormItem, FormText, TextInput } = CommonComponents; 102 const { value, displayName, description } = useConfigEntry<string>( 103 ext.id, 104 name 105 ); 106 107 return ( 108 <FormItem className={Margins.marginTop20} title={displayName}> 109 {description && ( 110 <FormText className={Margins.marginBottom8}>{description}</FormText> 111 )} 112 <TextInput 113 value={value ?? ""} 114 onChange={(value: string) => { 115 if (disabled) return; 116 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value); 117 }} 118 /> 119 </FormItem> 120 ); 121 } 122 123 function Select({ ext, name, setting, disabled }: SettingsProps) { 124 const { FormItem, FormText, SingleSelect } = CommonComponents; 125 const { value, displayName, description } = useConfigEntry<string>( 126 ext.id, 127 name 128 ); 129 130 const castedSetting = setting as SelectSettingType; 131 const options = castedSetting.options; 132 133 return ( 134 <FormItem className={Margins.marginTop20} title={displayName}> 135 {description && ( 136 <FormText className={Margins.marginBottom8}>{description}</FormText> 137 )} 138 <SingleSelect 139 autofocus={false} 140 clearable={false} 141 value={value ?? ""} 142 options={options.map((o: SelectOption) => 143 typeof o === "string" ? { value: o, label: o } : o 144 )} 145 onChange={(value: string) => { 146 if (disabled) return; 147 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value); 148 }} 149 /> 150 </FormItem> 151 ); 152 } 153 154 function MultiSelect({ ext, name, setting, disabled }: SettingsProps) { 155 const { FormItem, FormText, Select, useVariableSelect, multiSelect } = 156 CommonComponents; 157 const { value, displayName, description } = useConfigEntry< 158 string | string[] 159 >(ext.id, name); 160 161 const castedSetting = setting as MultiSelectSettingType; 162 const options = castedSetting.options; 163 164 return ( 165 <FormItem className={Margins.marginTop20} title={displayName}> 166 {description && ( 167 <FormText className={Margins.marginBottom8}>{description}</FormText> 168 )} 169 <Select 170 autofocus={false} 171 clearable={false} 172 closeOnSelect={false} 173 options={options.map((o: SelectOption) => 174 typeof o === "string" ? { value: o, label: o } : o 175 )} 176 {...useVariableSelect({ 177 onSelectInteraction: multiSelect, 178 value: new Set(Array.isArray(value) ? value : [value]), 179 onChange: (value: string) => { 180 if (disabled) return; 181 MoonbaseSettingsStore.setExtensionConfig( 182 ext.id, 183 name, 184 Array.from(value) 185 ); 186 } 187 })} 188 /> 189 </FormItem> 190 ); 191 } 192 193 const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0] 194 .exports; 195 const CircleXIcon = spacepack.findByCode(CircleXIconSVG)[0].exports.default; 196 function RemoveEntryButton({ 197 onClick, 198 disabled 199 }: { 200 onClick: () => void; 201 disabled: boolean; 202 }) { 203 const { Tooltip, Clickable } = CommonComponents; 204 return ( 205 <div className={RemoveButtonClasses.removeButtonContainer}> 206 <Tooltip text="Remove entry" position="top"> 207 {(props: any) => ( 208 <Clickable 209 {...props} 210 className={RemoveButtonClasses.removeButton} 211 onClick={onClick} 212 > 213 <CircleXIcon width={16} height={16} /> 214 </Clickable> 215 )} 216 </Tooltip> 217 </div> 218 ); 219 } 220 221 function List({ ext, name, setting, disabled }: SettingsProps) { 222 const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents; 223 const { value, displayName, description } = useConfigEntry<string[]>( 224 ext.id, 225 name 226 ); 227 228 const entries = value ?? []; 229 const updateConfig = () => 230 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, entries); 231 232 return ( 233 <FormItem className={Margins.marginTop20} title={displayName}> 234 {description && ( 235 <FormText className={Margins.marginBottom4}>{description}</FormText> 236 )} 237 <Flex direction={Flex.Direction.VERTICAL}> 238 {entries.map((val, i) => ( 239 // FIXME: stylesheets 240 <div 241 key={i} 242 style={{ 243 display: "grid", 244 height: "32px", 245 gap: "8px", 246 gridTemplateColumns: "1fr 32px", 247 alignItems: "center" 248 }} 249 > 250 <TextInput 251 size={TextInput.Sizes.MINI} 252 value={val} 253 disabled={disabled} 254 onChange={(newVal: string) => { 255 entries[i] = newVal; 256 updateConfig(); 257 }} 258 /> 259 <RemoveEntryButton 260 disabled={disabled} 261 onClick={() => { 262 entries.splice(i, 1); 263 updateConfig(); 264 }} 265 /> 266 </div> 267 ))} 268 269 <Button 270 look={Button.Looks.FILLED} 271 color={Button.Colors.GREEN} 272 size={Button.Sizes.SMALL} 273 disabled={disabled} 274 className={Margins.marginTop8} 275 onClick={() => { 276 entries.push(""); 277 updateConfig(); 278 }} 279 > 280 Add new entry 281 </Button> 282 </Flex> 283 </FormItem> 284 ); 285 } 286 287 function Dictionary({ ext, name, setting, disabled }: SettingsProps) { 288 const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents; 289 const { value, displayName, description } = useConfigEntry< 290 Record<string, string> 291 >(ext.id, name); 292 293 const entries = Object.entries(value ?? {}); 294 const updateConfig = () => 295 MoonbaseSettingsStore.setExtensionConfig( 296 ext.id, 297 name, 298 Object.fromEntries(entries) 299 ); 300 301 return ( 302 <FormItem className={Margins.marginTop20} title={displayName}> 303 {description && ( 304 <FormText className={Margins.marginBottom4}>{description}</FormText> 305 )} 306 <Flex direction={Flex.Direction.VERTICAL}> 307 {entries.map(([key, val], i) => ( 308 // FIXME: stylesheets 309 <div 310 key={i} 311 style={{ 312 display: "grid", 313 height: "32px", 314 gap: "8px", 315 gridTemplateColumns: "1fr 1fr 32px", 316 alignItems: "center" 317 }} 318 > 319 <TextInput 320 size={TextInput.Sizes.MINI} 321 value={key} 322 disabled={disabled} 323 onChange={(newKey: string) => { 324 entries[i][0] = newKey; 325 updateConfig(); 326 }} 327 /> 328 <TextInput 329 size={TextInput.Sizes.MINI} 330 value={val} 331 disabled={disabled} 332 onChange={(newValue: string) => { 333 entries[i][1] = newValue; 334 updateConfig(); 335 }} 336 /> 337 <RemoveEntryButton 338 disabled={disabled} 339 onClick={() => { 340 entries.splice(i, 1); 341 updateConfig(); 342 }} 343 /> 344 </div> 345 ))} 346 347 <Button 348 look={Button.Looks.FILLED} 349 color={Button.Colors.GREEN} 350 size={Button.Sizes.SMALL} 351 className={Margins.marginTop8} 352 disabled={disabled} 353 onClick={() => { 354 entries.push([`entry-${entries.length}`, ""]); 355 updateConfig(); 356 }} 357 > 358 Add new entry 359 </Button> 360 </Flex> 361 </FormItem> 362 ); 363 } 364 365 function Setting({ ext, name, setting, disabled }: SettingsProps) { 366 const elements: Partial<Record<ExtensionSettingType, SettingsComponent>> = { 367 [ExtensionSettingType.Boolean]: Boolean, 368 [ExtensionSettingType.Number]: Number, 369 [ExtensionSettingType.String]: String, 370 [ExtensionSettingType.Select]: Select, 371 [ExtensionSettingType.MultiSelect]: MultiSelect, 372 [ExtensionSettingType.List]: List, 373 [ExtensionSettingType.Dictionary]: Dictionary 374 }; 375 const element = elements[setting.type]; 376 if (element == null) return <></>; 377 return React.createElement(element, { ext, name, setting, disabled }); 378 } 379 380 return function Settings({ ext }: { ext: MoonbaseExtension }) { 381 const { Flex } = CommonComponents; 382 return ( 383 <Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}> 384 {Object.entries(ext.manifest.settings!).map(([name, setting]) => ( 385 <Setting 386 ext={ext} 387 key={name} 388 name={name} 389 setting={setting} 390 disabled={ext.state === ExtensionState.NotDownloaded} 391 /> 392 ))} 393 </Flex> 394 ); 395 }; 396};