implement select

This commit is contained in:
MapleLeaf
2021-12-27 00:48:30 -06:00
parent 4f978c101a
commit ceb8e78028
14 changed files with 553 additions and 31 deletions

View File

@@ -18,7 +18,7 @@ export class DiscordJsAdapter implements Adapter<Discord.CommandInteraction> {
listener: (interaction: ComponentInteraction) => void,
) {
this.client.on("interactionCreate", (interaction) => {
if (interaction.isButton()) {
if (interaction.isMessageComponent()) {
listener(createReacordComponentInteraction(interaction))
}
})
@@ -56,15 +56,32 @@ export class DiscordJsAdapter implements Adapter<Discord.CommandInteraction> {
function createReacordComponentInteraction(
interaction: Discord.MessageComponentInteraction,
): ComponentInteraction {
return {
type: "button",
id: interaction.id,
channelId: interaction.channelId,
customId: interaction.customId,
update: async (options) => {
await interaction.update(getDiscordMessageOptions(options))
},
if (interaction.isButton()) {
return {
type: "button",
id: interaction.id,
channelId: interaction.channelId,
customId: interaction.customId,
update: async (options) => {
await interaction.update(getDiscordMessageOptions(options))
},
}
}
if (interaction.isSelectMenu()) {
return {
type: "select",
id: interaction.id,
channelId: interaction.channelId,
customId: interaction.customId,
values: interaction.values,
update: async (options) => {
await interaction.update(getDiscordMessageOptions(options))
},
}
}
raise(`Unsupported component interaction type: ${interaction.type}`)
}
function createReacordMessage(message: Discord.Message): Message {
@@ -94,19 +111,33 @@ function getDiscordMessageOptions(
embeds: options.embeds,
components: options.actionRows.map((row) => ({
type: "ACTION_ROW",
components: row.map((component) => {
if (component.type === "button") {
return {
type: "BUTTON",
customId: component.customId,
label: component.label ?? "",
style: toUpper(component.style ?? "secondary"),
disabled: component.disabled,
emoji: component.emoji,
components: row.map(
(component): Discord.MessageActionRowComponentOptions => {
if (component.type === "button") {
return {
type: "BUTTON",
customId: component.customId,
label: component.label ?? "",
style: toUpper(component.style ?? "secondary"),
disabled: component.disabled,
emoji: component.emoji,
}
}
}
raise(`Unsupported component type: ${component.type}`)
}),
if (component.type === "select") {
return {
...component,
type: "SELECT_MENU",
options: component.options.map((option) => ({
...option,
default: component.values?.includes(option.value),
})),
}
}
raise(`Unsupported component type: ${component.type}`)
},
),
})),
}
}

View File

@@ -0,0 +1,14 @@
import type { MessageSelectOptionOptions } from "../../internal/message"
import { Node } from "../../internal/node"
import type { OptionProps } from "./option"
export class OptionNode extends Node<OptionProps> {
get options(): MessageSelectOptionOptions {
return {
label: this.props.children || this.props.label || this.props.value || "",
value: this.props.value,
description: this.props.description,
emoji: this.props.emoji,
}
}
}

View File

@@ -0,0 +1,17 @@
import React from "react"
import { ReacordElement } from "../../internal/element"
import { OptionNode } from "./option-node"
export type OptionProps = {
label?: string
children?: string
value: string
description?: string
emoji?: string
}
export function Option(props: OptionProps) {
return (
<ReacordElement props={props} createNode={() => new OptionNode(props)} />
)
}

View File

@@ -0,0 +1,89 @@
import { nanoid } from "nanoid"
import type { ReactNode } from "react"
import React from "react"
import { isInstanceOf } from "../../../helpers/is-instance-of"
import { ReacordElement } from "../../internal/element.js"
import type { ComponentInteraction } from "../../internal/interaction"
import type { MessageOptions } from "../../internal/message"
import { getNextActionRow } from "../../internal/message"
import { Node } from "../../internal/node.js"
import { OptionNode } from "./option-node"
export type SelectProps = {
children?: ReactNode
value?: string
values?: string[]
placeholder?: string
multiple?: boolean
minValues?: number
maxValues?: number
disabled?: boolean
onSelect?: (event: SelectEvent) => void
onSelectValue?: (value: string) => void
onSelectMultiple?: (values: string[]) => void
}
export type SelectEvent = {
values: string[]
}
export function Select(props: SelectProps) {
return (
<ReacordElement props={props} createNode={() => new SelectNode(props)}>
{props.children}
</ReacordElement>
)
}
class SelectNode extends Node<SelectProps> {
readonly customId = nanoid()
override modifyMessageOptions(message: MessageOptions): void {
const actionRow = getNextActionRow(message)
const options = [...this.children]
.filter(isInstanceOf(OptionNode))
.map((node) => node.options)
const {
multiple,
value,
values,
minValues = 0,
maxValues = 25,
children,
onSelect,
onSelectValue,
onSelectMultiple,
...props
} = this.props
actionRow.push({
...props,
type: "select",
customId: this.customId,
options,
values: [...(values || []), ...(value ? [value] : [])],
minValues: multiple ? minValues : undefined,
maxValues: multiple ? Math.max(minValues, maxValues) : undefined,
})
}
override handleComponentInteraction(
interaction: ComponentInteraction,
): boolean {
if (
interaction.type === "select" &&
interaction.customId === this.customId &&
!this.props.disabled
) {
this.props.onSelect?.({ values: interaction.values })
this.props.onSelectMultiple?.(interaction.values)
if (interaction.values[0]) {
this.props.onSelectValue?.(interaction.values[0])
}
return true
}
return false
}
}