embed children

This commit is contained in:
MapleLeaf
2021-12-25 03:54:35 -06:00
parent 6f3c97812c
commit bce472ab37
7 changed files with 130 additions and 44 deletions

27
src.new/container.ts Normal file
View File

@@ -0,0 +1,27 @@
export class Container<T> {
private items: T[] = []
add(...items: T[]) {
this.items.push(...items)
}
addBefore(item: T, before: T) {
let index = this.items.indexOf(before)
if (index === -1) {
index = this.items.length
}
this.items.splice(index, 0, item)
}
remove(toRemove: T) {
this.items = this.items.filter((item) => item !== toRemove)
}
clear() {
this.items = []
}
[Symbol.iterator]() {
return this.items[Symbol.iterator]()
}
}

11
src.new/element.tsx Normal file
View File

@@ -0,0 +1,11 @@
import type { ReactNode } from "react"
import React from "react"
import type { Node } from "./node.js"
export function ReacordElement<Props>(props: {
props: Props
createNode: () => Node<Props>
children?: ReactNode
}) {
return React.createElement("reacord-element", props)
}

View File

@@ -1,9 +1,9 @@
import type { MessageOptions } from "discord.js"
import type { MessageEmbedOptions, MessageOptions } from "discord.js"
import React from "react"
import { ReacordElement } from "./element.js"
import { Node } from "./node.js"
export type EmbedProps = {
title?: string
description?: string
url?: string
timestamp?: Date
@@ -23,22 +23,78 @@ export type EmbedProps = {
url?: string
iconURL?: string
}
fields?: Array<{
name: string
value: string
inline?: boolean
}>
children?: React.ReactNode
}
export function Embed(props: EmbedProps) {
return (
<reacord-element props={props} createNode={() => new EmbedNode(props)} />
<ReacordElement props={props} createNode={() => new EmbedNode(props)}>
{props.children}
</ReacordElement>
)
}
class EmbedNode extends Node<EmbedProps> {
override modifyMessageOptions(options: MessageOptions): void {
const embed = { ...this.props }
for (const child of this.children) {
if (child instanceof EmbedChildNode) {
child.modifyEmbedOptions(embed)
}
}
options.embeds ??= []
options.embeds.push(this.props)
options.embeds.push(embed)
}
}
abstract class EmbedChildNode<Props> extends Node<Props> {
abstract modifyEmbedOptions(options: MessageEmbedOptions): void
}
export type EmbedTitleProps = {
children: string
url?: string
}
Embed.Title = function Title(props: EmbedTitleProps) {
return (
<ReacordElement
props={props}
createNode={() => new EmbedTitleNode(props)}
/>
)
}
class EmbedTitleNode extends EmbedChildNode<EmbedTitleProps> {
override modifyEmbedOptions(options: MessageEmbedOptions): void {
options.title = this.props.children
options.url = this.props.url
}
}
export type EmbedFieldProps = {
name: string
inline?: boolean
children: string
}
Embed.Field = function Field(props: EmbedFieldProps) {
return (
<ReacordElement
props={props}
createNode={() => new EmbedFieldNode(props)}
/>
)
}
class EmbedFieldNode extends EmbedChildNode<EmbedFieldProps> {
override modifyEmbedOptions(options: MessageEmbedOptions): void {
options.fields ??= []
options.fields.push({
name: this.props.name,
value: this.props.children,
inline: this.props.inline,
})
}
}

View File

@@ -1,7 +1,9 @@
/* eslint-disable class-methods-use-this */
import type { MessageComponentInteraction, MessageOptions } from "discord.js"
import { Container } from "./container.js"
export abstract class Node<Props> {
readonly children = new Container<Node<unknown>>()
protected props: Props
constructor(initialProps: Props) {

View File

@@ -53,16 +53,29 @@ const config: HostConfig<
shouldSetTextContent: () => false,
clearContainer: (renderer) => {
renderer.clear()
renderer.nodes.clear()
},
appendChildToContainer: (renderer, child) => {
renderer.add(child)
renderer.nodes.add(child)
},
removeChildFromContainer: (renderer, child) => {
renderer.remove(child)
renderer.nodes.remove(child)
},
insertInContainerBefore: (renderer, child, before) => {
renderer.addBefore(child, before)
renderer.nodes.addBefore(child, before)
},
appendInitialChild: (parent, child) => {
parent.children.add(child)
},
appendChild: (parent, child) => {
parent.children.add(child)
},
removeChild: (parent, child) => {
parent.children.remove(child)
},
insertBefore: (parent, child, before) => {
parent.children.addBefore(child, before)
},
// eslint-disable-next-line unicorn/no-null
@@ -83,7 +96,6 @@ const config: HostConfig<
preparePortalMount: () => raise("Portals are not supported"),
getPublicInstance: () => raise("Refs are currently not supported"),
appendInitialChild: () => raise("not implemented"),
finalizeInitialChildren: () => false,
}

View File

@@ -3,34 +3,15 @@ import type {
MessageComponentInteraction,
MessageOptions,
} from "discord.js"
import { Container } from "./container.js"
import type { Node } from "./node.js"
export class Renderer {
private nodes: Array<Node<unknown>> = []
readonly nodes = new Container<Node<unknown>>()
private componentInteraction?: MessageComponentInteraction
constructor(private interaction: CommandInteraction) {}
add(node: Node<unknown>) {
this.nodes.push(node)
}
addBefore(node: Node<unknown>, before: Node<unknown>) {
let index = this.nodes.indexOf(before)
if (index === -1) {
index = this.nodes.length
}
this.nodes.splice(index, 0, node)
}
remove(node: Node<unknown>) {
this.nodes = this.nodes.filter((n) => n !== node)
}
clear() {
this.nodes = []
}
render() {
const options = this.getMessageOptions()
if (this.componentInteraction) {