generated from sapphiredev/sapphire-template
-
-
Notifications
You must be signed in to change notification settings - Fork 39
/
PaginatedFieldMessageEmbed.ts
180 lines (165 loc) · 6.15 KB
/
PaginatedFieldMessageEmbed.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import { isFunction } from '@sapphire/utilities';
import { EmbedBuilder, type EmbedData } from 'discord.js';
import { PaginatedMessage } from './PaginatedMessage';
/**
* This is a utility of {@link PaginatedMessage}, except it exclusively adds pagination inside a field of an embed.
* You must either use this class directly or extend it.
*
* It differs from PaginatedMessageEmbedFields as the items here are the shape you want, and are then concatenated
* in a single field with a given formatter function, whereas PaginatedMessageEmbedFields takes fields as the items
* and add them to the embed.
*
* @example
* ```typescript
* import { PaginatedFieldMessageEmbed } from '@sapphire/discord.js-utilities';
*
* new PaginatedFieldMessageEmbed()
* .setTitleField('Test pager field')
* .setTemplate({ embed })
* .setItems([
* { title: 'Sapphire Framework', value: 'discord.js Framework' },
* { title: 'Sapphire Framework 2', value: 'discord.js Framework 2' },
* { title: 'Sapphire Framework 3', value: 'discord.js Framework 3' }
* ])
* .formatItems((item) => `${item.title}\n${item.value}`)
* .setItemsPerPage(2)
* .make()
* .run(message);
* ```
*/
export class PaginatedFieldMessageEmbed<T> extends PaginatedMessage {
private embedTemplate: EmbedBuilder = new EmbedBuilder();
private totalPages = 0;
private items: T[] = [];
private itemsPerPage = 10;
private fieldTitle = '';
/**
* Set the items to paginate.
* @param items The pages to set
*/
public setItems(items: T[]) {
this.items = items;
return this;
}
/**
* Set the title of the embed field that will be used to paginate the items.
* @param title The field title
*/
public setTitleField(title: string) {
this.fieldTitle = title;
return this;
}
/**
* Sets the amount of items that should be shown per page.
* @param itemsPerPage The number of items
*/
public setItemsPerPage(itemsPerPage: number) {
this.itemsPerPage = itemsPerPage;
return this;
}
/**
* Sets the template to be used to display the embed fields as pages. This template can either be set from a template {@link EmbedBuilder} instance or an object with embed options.
*
* @param template EmbedBuilder
*
* @example
* ```typescript
* import { PaginatedFieldMessageEmbed } from '@sapphire/discord.js-utilities';
* import { EmbedBuilder } from 'discord.js';
*
* new PaginatedFieldMessageEmbed().setTemplate(new EmbedBuilder().setTitle('Test pager embed')).make().run(message);
* ```
*
* @example
* ```typescript
* import { PaginatedFieldMessageEmbed } from '@sapphire/discord.js-utilities';
*
* new PaginatedFieldMessageEmbed().setTemplate({ title: 'Test pager embed' }).make().run(message);
* ```
*/
public setTemplate(template: EmbedData | EmbedBuilder | ((embed: EmbedBuilder) => EmbedBuilder)) {
this.embedTemplate = this.resolveTemplate(template);
return this;
}
/**
* Sets a format callback that will be mapped to each embed field in the array of items when the embed is paginated. This should convert each item to a format that is either text itself or can be serialized as text.
*
* @example
* ```typescript
* import { PaginatedFieldMessageEmbed } from '@sapphire/discord.js-utilities';
*
* new PaginatedFieldMessageEmbed()
* .setTitleField('Test field')
* .setTemplate({ embed })
* .setItems([
* { title: 'Sapphire Framework', value: 'discord.js Framework' },
* { title: 'Sapphire Framework 2', value: 'discord.js Framework 2' },
* { title: 'Sapphire Framework 3', value: 'discord.js Framework 3' }
* ])
* .formatItems((item) => `${item.title}\n${item.value}`)
* .make()
* .run(message);
* ```
* @param formatter The formatter callback to be applied to each embed item
*/
public formatItems(formatter: (item: T, index: number, array: T[]) => any) {
this.items = this.items.map(formatter);
return this;
}
/**
* Build the pages of the given array.
*
* You must call the {@link PaginatedFieldMessageEmbed.make} and {@link PaginatedFieldMessageEmbed.run} methods last, in that order, for the pagination to work.
*
* @example
* ```typescript
* import { PaginatedFieldMessageEmbed } from '@sapphire/discord.js-utilities';
*
* new PaginatedFieldMessageEmbed()
* .setTitleField('Test field')
* .setTemplate({ embed })
* .setItems([
* { title: 'Sapphire Framework', value: 'discord.js Framework' },
* { title: 'Sapphire Framework 2', value: 'discord.js Framework 2' },
* { title: 'Sapphire Framework 3', value: 'discord.js Framework 3' }
* ])
* .formatItems((item) => `${item.title}\n${item.value}`)
* .make()
* .run(message);
* ```
*/
public make() {
if (!this.fieldTitle.length) throw new Error('The title of the field to format must have a value.');
if (!this.items.length) throw new Error('The items array is empty.');
if (this.items.some((x) => !x)) throw new Error('The format of the array items is incorrect.');
this.totalPages = Math.ceil(this.items.length / this.itemsPerPage);
this.generatePages();
return this;
}
private generatePages() {
const template = this.embedTemplate instanceof EmbedBuilder ? this.embedTemplate.toJSON() : this.embedTemplate;
for (let i = 0; i < this.totalPages; i++) {
const clonedTemplate = new EmbedBuilder(template);
const fieldsClone = this.embedTemplate.data.fields ?? [];
clonedTemplate.data.fields = [];
if (!clonedTemplate.data.color) clonedTemplate.setColor('Random');
const data = this.paginateArray(this.items, i, this.itemsPerPage);
this.addPage({
embeds: [clonedTemplate.addFields({ name: this.fieldTitle, value: data.join('\n'), inline: false }).addFields(fieldsClone)]
});
}
}
private paginateArray(items: T[], currentPage: number, perPageItems: number) {
const offset = currentPage * perPageItems;
return items.slice(offset, offset + perPageItems);
}
private resolveTemplate(template: EmbedBuilder | EmbedData | ((embed: EmbedBuilder) => EmbedBuilder)) {
if (template instanceof EmbedBuilder) {
return template;
}
if (isFunction(template)) {
return template(new EmbedBuilder());
}
return new EmbedBuilder(template);
}
}