add JSX components v2

This commit is contained in:
nin0 2025-05-15 07:10:53 -04:00
parent b5dd0ed413
commit 4b1227149f
Signed by: nin0
SSH key fingerprint: SHA256:NOoDnFVvZNFvqfXCIhzr6oCTDImZAbTTuyAysZ8Ufk8
25 changed files with 1080 additions and 3 deletions

View file

@ -0,0 +1,13 @@
import { ComponentTypes, MessageActionRow, MessageActionRowComponent } from "oceanic.js";
import { childrenToArray } from "./utils";
export type ActionRowProps = Omit<MessageActionRow, "type" | "components"> & { children?: MessageActionRowComponent | MessageActionRowComponent[] };
export function ActionRow(props: ActionRowProps): MessageActionRow {
return {
type: ComponentTypes.ACTION_ROW,
components: childrenToArray(props.children),
...props
};
}

16
components-jsx/Button.tsx Normal file
View file

@ -0,0 +1,16 @@
import { ButtonComponent, ComponentTypes, TextButton, URLButton } from "oceanic.js";
import { childrenToString } from "./utils";
export { ButtonStyles } from "oceanic.js";
type Button = Omit<TextButton, "type" | "label"> | Omit<URLButton, "type" | "label">;
export type ButtonProps = Button & { children?: any; };
export function Button({ children, ...props }: ButtonProps): ButtonComponent {
return {
type: ComponentTypes.BUTTON,
label: childrenToString("Button", children) ?? undefined,
...props
};
}

View file

@ -0,0 +1,14 @@
import { CreateMessageOptions, EditMessageOptions, MessageComponent, MessageFlags } from "oceanic.js";
import { childrenToArray } from "./utils";
type MessageOptions = CreateMessageOptions | EditMessageOptions;
export type ComponentMessageProps = MessageOptions & { children?: MessageComponent[]; };
export function ComponentMessage({ children, flags, ...props }: ComponentMessageProps): MessageOptions {
return {
flags: MessageFlags.IS_COMPONENTS_V2 | (flags ?? 0),
components: childrenToArray(children),
...props
};
}

View file

@ -0,0 +1,13 @@
import { ComponentTypes, ContainerComponent } from "oceanic.js";
import { childrenToArray } from "./utils";
export type ContainerProps = Omit<ContainerComponent, "type" | "components"> & { children?: ContainerComponent["components"] };
export function Container({ children, ...props }: ContainerProps): ContainerComponent {
return {
type: ComponentTypes.CONTAINER,
components: childrenToArray(children),
...props
};
}

18
components-jsx/File.tsx Normal file
View file

@ -0,0 +1,18 @@
import { ComponentTypes, FileComponent } from "oceanic.js";
import { MediaItem } from "./MediaItem";
interface FileProps {
filename: string;
id?: number;
spoiler?: boolean;
}
export function File({ filename, id, spoiler }: FileProps): FileComponent {
return {
type: ComponentTypes.FILE,
id,
spoiler,
file: <MediaItem url={`attachment://${filename}`} />,
};
}

View file

@ -0,0 +1,13 @@
import { ComponentTypes, MediaGalleryComponent } from "oceanic.js";
import { childrenToArray } from "./utils";
export type MediaGalleryProps = Omit<MediaGalleryComponent, "type" | "items"> & { children: MediaGalleryComponent["items"] };
export function MediaGallery(props: MediaGalleryProps): MediaGalleryComponent {
return {
type: ComponentTypes.MEDIA_GALLERY,
items: childrenToArray(props.children),
...props
};
}

View file

@ -0,0 +1,17 @@
import type { MediaGalleryItem as MediaGalleryItemComponent } from "oceanic.js";
import { MediaItem } from "./MediaItem";
export interface MediaGalleryItemProps {
url: string;
description?: string;
spoiler?: boolean;
}
export function MediaGalleryItem({ url, description, spoiler }: MediaGalleryItemProps): MediaGalleryItemComponent {
return {
media: <MediaItem url={url} />,
description,
spoiler,
};
}

View file

@ -0,0 +1,7 @@
import { UnfurledMediaItem } from "oceanic.js";
export type MediaItemProps = UnfurledMediaItem;
export function MediaItem(props: MediaItemProps): UnfurledMediaItem {
return props;
}

View file

@ -0,0 +1,10 @@
import { ComponentTypes, MentionableSelectMenu } from "oceanic.js";
export type MentionableSelectProps = Omit<MentionableSelectMenu, "type">;
export function MentionableSelect(props: MentionableSelectProps): MentionableSelectMenu {
return {
type: ComponentTypes.MENTIONABLE_SELECT,
...props
};
}

View file

@ -0,0 +1,10 @@
import { ComponentTypes, RoleSelectMenu } from "oceanic.js";
export type RoleSelectProps = Omit<RoleSelectMenu, "type">;
export function RoleSelect(props: RoleSelectProps): RoleSelectMenu {
return {
type: ComponentTypes.ROLE_SELECT,
...props
};
}

View file

@ -0,0 +1,16 @@
import { ButtonComponent, ComponentTypes, SectionComponent, TextDisplayComponent, ThumbnailComponent } from "oceanic.js";
import { childrenToArray } from "./utils";
export interface SectionProps {
children: TextDisplayComponent[]
accessory: ThumbnailComponent | ButtonComponent;
}
export function Section(props: SectionProps): SectionComponent {
return {
type: ComponentTypes.SECTION,
components: childrenToArray(props.children),
...props
};
}

View file

@ -0,0 +1,10 @@
import { ComponentTypes, SeparatorComponent } from "oceanic.js";
export type SeparatorProps = Omit<SeparatorComponent, "type">;
export function Separator(props: SeparatorProps): SeparatorComponent {
return {
type: ComponentTypes.SEPARATOR,
...props
};
}

View file

@ -0,0 +1,13 @@
import { ComponentTypes, StringSelectMenu } from "oceanic.js";
import { childrenToArray } from "./utils";
export type StringSelectProps = Omit<StringSelectMenu, "type" | "options"> & { children: StringSelectMenu["options"] };
export function StringSelect(props: StringSelectProps): StringSelectMenu {
return {
type: ComponentTypes.STRING_SELECT,
options: childrenToArray(props.children),
...props
};
}

View file

@ -0,0 +1,21 @@
import { ComponentTypes, TextDisplayComponent } from "oceanic.js";
import { childrenToString } from "./utils";
export interface TextDisplayProps {
children?: any;
id?: number;
}
export function TextDisplay({ children, id }: TextDisplayProps): TextDisplayComponent {
children = childrenToString("TextDisplay", children);
if (!children) {
throw new Error("TextDisplay requires at least one child");
}
return {
type: ComponentTypes.TEXT_DISPLAY,
content: children,
id,
};
}

View file

@ -0,0 +1,12 @@
import { ComponentTypes, TextInput } from "oceanic.js";
export { TextInputStyles } from "oceanic.js";
export type TextInputProps = Omit<TextInput, "type">;
export function TextInput(props: TextInputProps): TextInput {
return {
type: ComponentTypes.TEXT_INPUT,
...props
};
}

View file

@ -0,0 +1,11 @@
import { ComponentTypes, ThumbnailComponent } from "oceanic.js";
export type ThumbnailProps = Omit<ThumbnailComponent, "type" | "media"> & { children: ThumbnailComponent["media"] };
export function Thumbnail(props: ThumbnailProps): ThumbnailComponent {
return {
type: ComponentTypes.THUMBNAIL,
media: props.children,
...props
};
}

View file

@ -0,0 +1,10 @@
import { ComponentTypes, UserSelectMenu } from "oceanic.js";
export type UserSelectProps = Omit<UserSelectMenu, "type">;
export function UserSelect(props: UserSelectProps): UserSelectMenu {
return {
type: ComponentTypes.USER_SELECT,
...props
};
}

17
components-jsx/index.ts Normal file
View file

@ -0,0 +1,17 @@
export * from "./ActionRow";
export * from "./Button";
export * from "./ComponentMessage";
export * from "./Container";
export * from "./File";
export * from "./MediaGallery";
export * from "./MediaGalleryItem";
export * from "./MediaItem";
export * from "./MentionableSelect";
export * from "./RoleSelect";
export * from "./Section";
export * from "./Separator";
export * from "./StringSelect";
export * from "./TextDisplay";
export * from "./TextInput";
export * from "./Thumbnail";
export * from "./UserSelect";

11
components-jsx/runtime.ts Normal file
View file

@ -0,0 +1,11 @@
export const Fragment = Symbol("ComponentsJsx.Fragment");
export function createElement(type: typeof Fragment | ((props: any) => any), props: any, ...children: any[]) {
if (type === Fragment) {
return children;
}
props ??= {};
props.children = children;
return type(props);
}

29
components-jsx/utils.ts Normal file
View file

@ -0,0 +1,29 @@
const isFalseOrNullish = (value: any) => value === false || value == null;
const isNotFalseOrNullish = (value: any) => !isFalseOrNullish(value);
export function filterChildren(children: any[]) {
return children.filter(isNotFalseOrNullish);
}
export function childrenToString(name: string, children: any) {
if (Array.isArray(children)) {
return filterChildren(children).join("\n");
}
if (typeof children === "string") {
return children;
}
if (isFalseOrNullish(children)) {
return null;
}
throw new Error(`${name} children must be a string or an array of strings`);
}
export function childrenToArray(children: any) {
if (Array.isArray(children)) {
return filterChildren(children);
}
if (isFalseOrNullish(children)) {
return [];
}
return [children];
}