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

View File

@@ -10,15 +10,12 @@ export function Counter() {
<> <>
this button was clicked {count} times this button was clicked {count} times
{embedVisible && ( {embedVisible && (
<Embed <Embed>
title="the counter" <Embed.Title>the counter</Embed.Title>
fields={[ <Embed.Field name="is it even?">
{ {count % 2 === 0 ? "yes" : "no"}
name: "is it even?", </Embed.Field>
value: count % 2 === 0 ? "yes" : "no", </Embed>
},
]}
/>
)} )}
{embedVisible && ( {embedVisible && (
<Button label="hide embed" onClick={() => setEmbedVisible(false)} /> <Button label="hide embed" onClick={() => setEmbedVisible(false)} />

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 React from "react"
import { ReacordElement } from "./element.js"
import { Node } from "./node.js" import { Node } from "./node.js"
export type EmbedProps = { export type EmbedProps = {
title?: string
description?: string description?: string
url?: string url?: string
timestamp?: Date timestamp?: Date
@@ -23,22 +23,78 @@ export type EmbedProps = {
url?: string url?: string
iconURL?: string iconURL?: string
} }
fields?: Array<{ children?: React.ReactNode
name: string
value: string
inline?: boolean
}>
} }
export function Embed(props: EmbedProps) { export function Embed(props: EmbedProps) {
return ( return (
<reacord-element props={props} createNode={() => new EmbedNode(props)} /> <ReacordElement props={props} createNode={() => new EmbedNode(props)}>
{props.children}
</ReacordElement>
) )
} }
class EmbedNode extends Node<EmbedProps> { class EmbedNode extends Node<EmbedProps> {
override modifyMessageOptions(options: MessageOptions): void { 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 ??= []
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 */ /* eslint-disable class-methods-use-this */
import type { MessageComponentInteraction, MessageOptions } from "discord.js" import type { MessageComponentInteraction, MessageOptions } from "discord.js"
import { Container } from "./container.js"
export abstract class Node<Props> { export abstract class Node<Props> {
readonly children = new Container<Node<unknown>>()
protected props: Props protected props: Props
constructor(initialProps: Props) { constructor(initialProps: Props) {

View File

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

View File

@@ -3,34 +3,15 @@ import type {
MessageComponentInteraction, MessageComponentInteraction,
MessageOptions, MessageOptions,
} from "discord.js" } from "discord.js"
import { Container } from "./container.js"
import type { Node } from "./node.js" import type { Node } from "./node.js"
export class Renderer { export class Renderer {
private nodes: Array<Node<unknown>> = [] readonly nodes = new Container<Node<unknown>>()
private componentInteraction?: MessageComponentInteraction private componentInteraction?: MessageComponentInteraction
constructor(private interaction: CommandInteraction) {} 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() { render() {
const options = this.getMessageOptions() const options = this.getMessageOptions()
if (this.componentInteraction) { if (this.componentInteraction) {