wip: components library

This commit is contained in:
Rie Takahashi 2022-10-24 18:04:25 +01:00
parent 5fac8be0ae
commit 0e7bd87cee
9 changed files with 200 additions and 17 deletions

@ -16,9 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { PluginOptionSelect } from "../../../utils/types"; import { FormSection, FormText, FormTitle } from "@components/Forms";
import { Forms, React, Select } from "../../../webpack/common"; import Select from "@components/Select";
import { ISettingElementProps } from "."; import { ISettingElementProps } from ".";
import { PluginOptionSelect } from "../../../utils/types";
import { React } from "../../../webpack/common";
export function SettingSelectComponent({ option, pluginSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) { export function SettingSelectComponent({ option, pluginSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
const def = pluginSettings[id] ?? option.options?.find(o => o.default)?.value; const def = pluginSettings[id] ?? option.options?.find(o => o.default)?.value;
@ -41,8 +44,8 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr
} }
return ( return (
<Forms.FormSection> <FormSection>
<Forms.FormTitle>{option.description}</Forms.FormTitle> <FormTitle>{option.description}</FormTitle>
<Select <Select
isDisabled={option.disabled?.() ?? false} isDisabled={option.disabled?.() ?? false}
options={option.options} options={option.options}
@ -54,7 +57,7 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr
serialize={v => String(v)} serialize={v => String(v)}
{...option.componentProps} {...option.componentProps}
/> />
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>} {error && <FormText style={{ color: "var(--text-danger)" }}>{error}</FormText>}
</Forms.FormSection> </FormSection>
); );
} }

@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { FormDivider, FormSection, FormText, FormTitle } from "@components/Forms";
import Plugins from "~plugins"; import Plugins from "~plugins";
import { showNotice } from "../../api/Notices"; import { showNotice } from "../../api/Notices";
@ -26,7 +27,7 @@ import { ChangeList } from "../../utils/ChangeList";
import { classes, lazyWebpack } from "../../utils/misc"; import { classes, lazyWebpack } from "../../utils/misc";
import { Plugin } from "../../utils/types"; import { Plugin } from "../../utils/types";
import { filters } from "../../webpack"; import { filters } from "../../webpack";
import { Alerts, Button, Forms, Margins, Parser, React, Switch, Text, TextInput, Toasts, Tooltip } from "../../webpack/common"; import { Alerts, Button, Margins, Parser, React, Switch, Text, TextInput, Toasts, Tooltip } from "../../webpack/common";
import ErrorBoundary from "../ErrorBoundary"; import ErrorBoundary from "../ErrorBoundary";
import { ErrorCard } from "../ErrorCard"; import { ErrorCard } from "../ErrorCard";
import { Flex } from "../Flex"; import { Flex } from "../Flex";
@ -235,10 +236,10 @@ export default ErrorBoundary.wrap(function Settings() {
}; };
return ( return (
<Forms.FormSection tag="h1" title="Vencord"> <FormSection tag="h1" title="Vencord">
<Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}> <FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
Plugins Plugins
</Forms.FormTitle> </FormTitle>
<ReloadRequiredCard plugins={[...changes.getChanges()]} style={{ marginBottom: 16 }} /> <ReloadRequiredCard plugins={[...changes.getChanges()]} style={{ marginBottom: 16 }} />
@ -275,10 +276,10 @@ export default ErrorBoundary.wrap(function Settings() {
: <Text variant="text-md/normal">No plugins meet search criteria.</Text> : <Text variant="text-md/normal">No plugins meet search criteria.</Text>
} }
</div> </div>
<Forms.FormDivider /> <FormDivider />
<Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}> <FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
Required Plugins Required Plugins
</Forms.FormTitle> </FormTitle>
<div style={styles.PluginsGrid}> <div style={styles.PluginsGrid}>
{sortedPlugins?.length ? sortedPlugins {sortedPlugins?.length ? sortedPlugins
.filter(a => a.required || dependencyCheck(a.name, depMap).length && pluginFilter(a)) .filter(a => a.required || dependencyCheck(a.name, depMap).length && pluginFilter(a))
@ -303,15 +304,15 @@ export default ErrorBoundary.wrap(function Settings() {
: <Text variant="text-md/normal">No plugins meet search criteria.</Text> : <Text variant="text-md/normal">No plugins meet search criteria.</Text>
} }
</div> </div>
</Forms.FormSection > </FormSection>
); );
}); });
function makeDependencyList(deps: string[]) { function makeDependencyList(deps: string[]) {
return ( return (
<React.Fragment> <React.Fragment>
<Forms.FormText>This plugin is required by:</Forms.FormText> <FormText>This plugin is required by:</FormText>
{deps.map((dep: string) => <Forms.FormText style={{ margin: "0 auto" }}>{dep}</Forms.FormText>)} {deps.map((dep: string) => <FormText style={{ margin: "0 auto" }}>{dep}</FormText>)}
</React.Fragment> </React.Fragment>
); );
} }

@ -33,6 +33,7 @@ export let UserStore: Stores.UserStore;
export let SelectedChannelStore: Stores.SelectedChannelStore; export let SelectedChannelStore: Stores.SelectedChannelStore;
export let ChannelStore: Stores.ChannelStore; export let ChannelStore: Stores.ChannelStore;
/** @deprecated import from "@components/Forms" instead */
export const Forms = {} as { export const Forms = {} as {
FormTitle: Components.FormTitle; FormTitle: Components.FormTitle;
FormSection: any; FormSection: any;
@ -47,6 +48,7 @@ export let Router: any;
export let TextInput: any; export let TextInput: any;
export let Text: (props: TextProps) => JSX.Element; export let Text: (props: TextProps) => JSX.Element;
/** @deprecated import from "@components/Select" instead */
export const Select = lazyWebpack(filters.byCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems")); export const Select = lazyWebpack(filters.byCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems"));
export const Slider = lazyWebpack(filters.byCode("closestMarkerIndex", "stickToMarkers")); export const Slider = lazyWebpack(filters.byCode("closestMarkerIndex", "stickToMarkers"));
@ -78,6 +80,7 @@ const ToastPosition = {
BOTTOM: 1 BOTTOM: 1
}; };
/** @deprecated import from "@components/Toasts" instead */
export const Toasts = { export const Toasts = {
Type: ToastType, Type: ToastType,
Position: ToastPosition, Position: ToastPosition,

@ -0,0 +1,64 @@
import { filters, proxyWaitFor } from "../webpack";
export enum Tag {
H1 = "h1",
H2 = "h2",
H3 = "h3",
H4 = "h4",
H5 = "h5",
}
interface FormTitleProps extends React.PropsWithChildren<React.HTMLProps<HTMLDivElement>> {
tag?: Tag | `${Tag}`;
disabled?: boolean;
required?: boolean;
error?: string;
faded?: boolean;
}
interface FormSectionProps extends React.PropsWithChildren {
title?: React.ReactNode;
icon?: React.ReactNode;
titleId?: string;
tag?: Tag | `${Tag}`;
titleClassName?: string;
}
export enum FormTextType {
DEFAULT = "default",
INPUT_PLACEHOLDER = "placeholder",
DESCRIPTION = "description",
LABEL_BOLD = "labelBold",
LABEL_SELECTED = "labelSelected",
LABEL_DESCRIPTOR = "labelDescriptor",
ERROR = "error",
SUCCESS = "success",
}
interface FormTextProps extends React.PropsWithChildren<React.HTMLProps<HTMLDivElement>> {
type?: FormTextType;
selectable?: boolean;
}
interface FormDividerProps {
className?: string;
style?: React.CSSProperties;
}
export const FormTitle: (props: FormTitleProps) => JSX.Element = proxyWaitFor(filters.byCode("errorSeparator"));
export const FormSection: (props: FormSectionProps) => JSX.Element = proxyWaitFor(filters.byCode("titleClassName", "sectionTitle"));
export const FormText: (props: FormTextProps) => JSX.Element = proxyWaitFor(m => m.Types?.INPUT_PLACEHOLDER);
export const FormDivider: (props: FormDividerProps) => JSX.Element = proxyWaitFor(m => {
if (typeof m !== "function") return false;
const s = m.toString();
return s.length < 200 && s.includes("divider");
});
export const Forms = {
FormTitle,
FormSection,
FormText,
FormDivider
};
export default Forms;

@ -0,0 +1,55 @@
import { lazyWebpack } from "src/utils";
import { filters } from "../webpack";
export enum Look {
FILLED,
CUSTOM
}
export enum Position {
TOP = "top",
LEFT = "left",
RIGHT = "right",
BOTTOM = "bottom",
CENTER = "center",
WINDOW_CENTER = "window_center",
}
export interface SelectOption {
value: any;
label: string;
disabled?: boolean;
key?: React.Key;
}
interface SelectProps {
options: SelectOption[];
placeholder?: string;
className?: string;
isDisabled?: boolean;
maxVisibleItems?: number;
look?: Look;
autoFocus?: boolean;
popoutWidth?: number;
clearable?: boolean;
onClose?(): void;
onOpen?(): void;
renderOptionLabel?(option: SelectOption): React.ReactNode;
renderOptionValue?(option: SelectOption[]): React.ReactNode;
popoutClassName?: string;
popoutPosition?: Position;
optionClassName?: string;
closeOnSelect?: boolean;
select?(value: any): void;
isSelected?(value: any): boolean;
serialize?(value: any): string;
clear?(): void;
hideIcon?: boolean;
"aria-label"?: string;
"aria-labelledby"?: string;
}
export const Select: (props: SelectProps) => JSX.Element = lazyWebpack(filters.byCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems"));
export default Select;

@ -0,0 +1,42 @@
import { lazyWebpack } from "../../utils";
import { filters } from "../webpack";
export enum ToastType {
MESSAGE = 0,
SUCCESS,
FAILURE,
CUSTOM
}
export enum ToastPosition {
TOP = 0,
BOTTOM,
}
export interface ToastOptions {
position?: ToastPosition;
timeout?: number;
duration?: number;
}
export interface ToastProps {
message: string;
type: ToastType;
id: string;
options?: ToastOptions;
}
const showToast = lazyWebpack(filters.byCode("currentToast?"));
const popToast = lazyWebpack(filters.byCode("currentToast:null"));
export const ToastAPI = {
show(props: ToastProps): void {
return showToast(props);
},
pop(): void {
return popToast();
}
};
export default ToastAPI;

@ -0,0 +1,4 @@
export * as Toasts from "./Toasts";
export * as Forms from "./Forms";
export * as Select from "./Select";

@ -180,6 +180,12 @@ export function waitFor(filter: string | string[] | FilterFn, callback: Callback
subscriptions.set(filter, callback); subscriptions.set(filter, callback);
} }
export function proxyWaitFor(filter: string | string[] | FilterFn, mapper = m => m) {
let v;
waitFor(filter, m => v = mapper(m));
return proxyLazy(() => v);
}
export function addListener(callback: CallbackFn) { export function addListener(callback: CallbackFn) {
listeners.add(callback); listeners.add(callback);
} }

@ -17,7 +17,12 @@
// https://esbuild.github.io/api/#jsx-factory // https://esbuild.github.io/api/#jsx-factory
"jsxFactory": "Vencord.Webpack.Common.React.createElement", "jsxFactory": "Vencord.Webpack.Common.React.createElement",
"jsxFragmentFactory": "Vencord.Webpack.Common.React.Fragment", "jsxFragmentFactory": "Vencord.Webpack.Common.React.Fragment",
"jsx": "react" "jsx": "react",
"baseUrl": ".",
"paths": {
"@components": ["src/webpack/components/index.ts"],
"@components/*": ["src/webpack/components/*"]
}
}, },
"include": ["src/**/*"] "include": ["src/**/*"]
} }