Skip to content

Commit 7242352

Browse files
authored
feat!: role gradient colors (#10961)
* feat: role gradient colors * refactor: `options.colors && { ... }` * refactor(Role)!: remove `color` * feat: `RoleColorsResolvable` * feat(Constants): add `HolographicStyle`
1 parent bc6005f commit 7242352

File tree

5 files changed

+118
-24
lines changed

5 files changed

+118
-24
lines changed

packages/discord.js/src/managers/GuildMemberRoleManager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class GuildMemberRoleManager extends DataManager {
7272
* @readonly
7373
*/
7474
get color() {
75-
const coloredRoles = this.cache.filter(role => role.color);
75+
const coloredRoles = this.cache.filter(role => role.colors.primaryColor);
7676
if (!coloredRoles.size) return null;
7777
return coloredRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev));
7878
}

packages/discord.js/src/managers/RoleManager.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,22 @@ class RoleManager extends CachedManager {
109109
* @returns {?Snowflake}
110110
*/
111111

112+
/**
113+
* @typedef {Object} RoleColorsResolvable
114+
* @property {ColorResolvable} primaryColor The primary color of the role
115+
* @property {ColorResolvable} [secondaryColor] The secondary color of the role.
116+
* This will make the role a gradient between the other provided colors
117+
* @property {ColorResolvable} [tertiaryColor] The tertiary color of the role.
118+
* When sending `tertiaryColor` the API enforces the role color to be a holographic style with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
119+
* These values are available as a constant: `Constants.HolographicStyle`
120+
*/
121+
112122
/**
113123
* Options used to create a new role.
114124
*
115125
* @typedef {Object} RoleCreateOptions
116126
* @property {string} [name] The name of the new role
117-
* @property {ColorResolvable} [color] The data to create the role with
127+
* @property {RoleColorsResolvable} [colors] The colors to create the role with
118128
* @property {boolean} [hoist] Whether or not the new role should be hoisted
119129
* @property {PermissionResolvable} [permissions] The permissions for the new role
120130
* @property {number} [position] The position of the new role
@@ -141,27 +151,47 @@ class RoleManager extends CachedManager {
141151
* // Create a new role with data and a reason
142152
* guild.roles.create({
143153
* name: 'Super Cool Blue People',
144-
* color: Colors.Blue,
145154
* reason: 'we needed a role for Super Cool People',
155+
* colors: {
156+
* primaryColor: Colors.Blue,
157+
* },
158+
* })
159+
* .then(console.log)
160+
* .catch(console.error);
161+
* @example
162+
* // Create a role with holographic colors
163+
* guild.roles.create({
164+
* name: 'Holographic Role',
165+
* reason: 'Creating a role with holographic effect',
166+
* colors: {
167+
* primaryColor: Constants.HolographicStyle.Primary,
168+
* secondaryColor: Constants.HolographicStyle.Secondary,
169+
* tertiaryColor: Constants.HolographicStyle.Tertiary,
170+
* },
146171
* })
147172
* .then(console.log)
148173
* .catch(console.error);
149174
*/
150175
async create(options = {}) {
151-
let { color, permissions, icon } = options;
176+
let { permissions, icon } = options;
152177
const { name, hoist, position, mentionable, reason, unicodeEmoji } = options;
153-
color &&= resolveColor(color);
154178
if (permissions !== undefined) permissions = new PermissionsBitField(permissions);
155179
if (icon) {
156180
const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL();
157181
icon = guildEmojiURL ? await resolveImage(guildEmojiURL) : await resolveImage(icon);
158182
if (typeof icon !== 'string') icon = undefined;
159183
}
160184

185+
const colors = options.colors && {
186+
primary_color: resolveColor(options.colors.primaryColor),
187+
secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
188+
tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
189+
};
190+
161191
const data = await this.client.rest.post(Routes.guildRoles(this.guild.id), {
162192
body: {
163193
name,
164-
color,
194+
colors,
165195
hoist,
166196
permissions,
167197
mentionable,
@@ -212,9 +242,15 @@ class RoleManager extends CachedManager {
212242
if (typeof icon !== 'string') icon = undefined;
213243
}
214244

245+
const colors = options.colors && {
246+
primary_color: resolveColor(options.colors.primaryColor),
247+
secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
248+
tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
249+
};
250+
215251
const body = {
216252
name: options.name,
217-
color: options.color === undefined ? undefined : resolveColor(options.color),
253+
colors,
218254
hoist: options.hoist,
219255
permissions: options.permissions === undefined ? undefined : new PermissionsBitField(options.permissions),
220256
mentionable: options.mentionable,

packages/discord.js/src/structures/Role.js

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,27 @@ class Role extends Base {
5757
this.name = data.name;
5858
}
5959

60-
if ('color' in data) {
60+
/**
61+
* @typedef {Object} RoleColors
62+
* @property {number} primaryColor The primary color of the role
63+
* @property {?number} secondaryColor The secondary color of the role.
64+
* This will make the role a gradient between the other provided colors
65+
* @property {?number} tertiaryColor The tertiary color of the role.
66+
* When sending `tertiaryColor` the API enforces the role color to be a holographic style with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
67+
* These values are available as a constant: `Constants.HolographicStyle`
68+
*/
69+
70+
if ('colors' in data) {
6171
/**
62-
* The base 10 color of the role
72+
* The colors of the role
6373
*
64-
* @type {number}
74+
* @type {RoleColors}
6575
*/
66-
this.color = data.color;
76+
this.colors = {
77+
primaryColor: data.colors.primary_color,
78+
secondaryColor: data.colors.secondary_color,
79+
tertiaryColor: data.colors.tertiary_color,
80+
};
6781
}
6882

6983
if ('hoist' in data) {
@@ -257,7 +271,7 @@ class Role extends Base {
257271
*
258272
* @typedef {Object} RoleData
259273
* @property {string} [name] The name of the role
260-
* @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number
274+
* @property {RoleColorsResolvable} [colors] The colors of the role
261275
* @property {boolean} [hoist] Whether or not the role should be hoisted
262276
* @property {number} [position] The position of the role
263277
* @property {PermissionResolvable} [permissions] The permissions of the role
@@ -315,19 +329,28 @@ class Role extends Base {
315329
}
316330

317331
/**
318-
* Sets a new color for the role.
332+
* Sets new colors for the role.
319333
*
320-
* @param {ColorResolvable} color The color of the role
321-
* @param {string} [reason] Reason for changing the role's color
334+
* @param {RoleColorsResolvable} colors The colors of the role
335+
* @param {string} [reason] Reason for changing the role's colors
322336
* @returns {Promise<Role>}
323337
* @example
324-
* // Set the color of a role
325-
* role.setColor('#FF0000')
326-
* .then(updated => console.log(`Set color of role to ${updated.color}`))
338+
* // Set the colors of a role
339+
* role.setColors({ primaryColor: '#FF0000', secondaryColor: '#00FF00', tertiaryColor: '#0000FF' })
340+
* .then(updated => console.log(`Set colors of role to ${updated.colors}`))
341+
* .catch(console.error);
342+
* @example
343+
* // Set holographic colors using constants
344+
* role.setColors({
345+
* primaryColor: Constants.HolographicStyle.Primary,
346+
* secondaryColor: Constants.HolographicStyle.Secondary,
347+
* tertiaryColor: Constants.HolographicStyle.Tertiary,
348+
* })
349+
* .then(updated => console.log(`Set holographic colors for role ${updated.name}`))
327350
* .catch(console.error);
328351
*/
329-
async setColor(color, reason) {
330-
return this.edit({ color, reason });
352+
async setColors(colors, reason) {
353+
return this.edit({ colors, reason });
331354
}
332355

333356
/**
@@ -475,7 +498,9 @@ class Role extends Base {
475498
role &&
476499
this.id === role.id &&
477500
this.name === role.name &&
478-
this.color === role.color &&
501+
this.colors.primaryColor === role.colors.primaryColor &&
502+
this.colors.secondaryColor === role.colors.secondaryColor &&
503+
this.colors.tertiaryColor === role.colors.tertiaryColor &&
479504
this.hoist === role.hoist &&
480505
this.position === role.position &&
481506
this.permissions.bitfield === role.permissions.bitfield &&

packages/discord.js/src/util/Constants.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,21 @@ exports.StickerFormatExtensionMap = {
222222
[StickerFormatType.GIF]: ImageFormat.GIF,
223223
};
224224

225+
/**
226+
* Holographic color values for role styling.
227+
* When using `tertiaryColor`, the API enforces these specific values for holographic effect.
228+
*
229+
* @typedef {Object} HolographicStyle
230+
* @property {number} Primary 11127295 (0xA9FFFF)
231+
* @property {number} Secondary 16759788 (0xFFCCCC)
232+
* @property {number} Tertiary 16761760 (0xFFE0A0)
233+
*/
234+
exports.HolographicStyle = {
235+
Primary: 11_127_295,
236+
Secondary: 16_759_788,
237+
Tertiary: 16_761_760,
238+
};
239+
225240
/**
226241
* @typedef {Object} Constants Constants that can be used in an enum or object-like way.
227242
* @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age
@@ -232,4 +247,5 @@ exports.StickerFormatExtensionMap = {
232247
* @property {VoiceBasedChannelTypes} VoiceBasedChannelTypes The types of channels that are voice-based
233248
* @property {SelectMenuTypes} SelectMenuTypes The types of components that are select menus.
234249
* @property {Object} StickerFormatExtensionMap A mapping between sticker formats and their respective image formats.
250+
* @property {HolographicStyle} HolographicStyle Holographic color values for role styling.
235251
*/

packages/discord.js/typings/index.d.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,9 +2823,21 @@ export class RichPresenceAssets {
28232823
public smallImageURL(options?: ImageURLOptions): string | null;
28242824
}
28252825

2826+
export interface RoleColors {
2827+
primaryColor: number;
2828+
secondaryColor: number | null;
2829+
tertiaryColor: number | null;
2830+
}
2831+
2832+
export interface RoleColorsResolvable {
2833+
primaryColor: ColorResolvable;
2834+
secondaryColor?: ColorResolvable;
2835+
tertiaryColor?: ColorResolvable;
2836+
}
2837+
28262838
export class Role extends Base {
28272839
private constructor(client: Client<true>, data: APIRole, guild: Guild);
2828-
public color: number;
2840+
public colors: RoleColors;
28292841
public get createdAt(): Date;
28302842
public get createdTimestamp(): number;
28312843
public get editable(): boolean;
@@ -2853,7 +2865,7 @@ export class Role extends Base {
28532865
channel: NonThreadGuildBasedChannel | Snowflake,
28542866
checkAdmin?: boolean,
28552867
): Readonly<PermissionsBitField>;
2856-
public setColor(color: ColorResolvable, reason?: string): Promise<Role>;
2868+
public setColors(colors: RoleColorsResolvable, reason?: string): Promise<Role>;
28572869
public setHoist(hoist?: boolean, reason?: string): Promise<Role>;
28582870
public setMentionable(mentionable?: boolean, reason?: string): Promise<Role>;
28592871
public setName(name: string, reason?: string): Promise<Role>;
@@ -3808,6 +3820,11 @@ export type UndeletableMessageType =
38083820

38093821
export const Constants: {
38103822
GuildTextBasedChannelTypes: GuildTextBasedChannelTypes[];
3823+
HolographicStyle: {
3824+
Primary: 11_127_295;
3825+
Secondary: 16_759_788;
3826+
Tertiary: 16_761_760;
3827+
};
38113828
MaxBulkDeletableMessageAge: 1_209_600_000;
38123829
NonSystemMessageTypes: NonSystemMessageType[];
38133830
SelectMenuTypes: SelectMenuType[];
@@ -6836,7 +6853,7 @@ export interface ResolvedOverwriteOptions {
68366853
}
68376854

68386855
export interface RoleData {
6839-
color?: ColorResolvable;
6856+
colors?: RoleColorsResolvable;
68406857
hoist?: boolean;
68416858
icon?: Base64Resolvable | BufferResolvable | EmojiResolvable | null;
68426859
mentionable?: boolean;

0 commit comments

Comments
 (0)