@kevlid/discordmenus 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevlid/discordmenus",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Components v2 settings menus for Discord bots",
5
5
  "main": "src/index.js",
6
6
  "types": "index.d.ts",
@@ -312,8 +312,8 @@ async function handleComponent(interaction, menu, menu_key, renderMenu) {
312
312
  }
313
313
  }
314
314
 
315
- const category = parseInt(options.get("cat") ?? 0);
316
- const page = parseInt(options.get("page") ?? 0);
315
+ let category = parseInt(options.get("cat") ?? 0);
316
+ let page = parseInt(options.get("page") ?? 0);
317
317
  const action = values[1];
318
318
 
319
319
  let result;
@@ -362,7 +362,15 @@ async function handleComponent(interaction, menu, menu_key, renderMenu) {
362
362
  ) {
363
363
  const optionKey = values[0];
364
364
  const selected = Array.isArray(interactionData.values) ? interactionData.values : [];
365
- const opt = menu.getOption(category, page, optionKey);
365
+ let opt = menu.getOption(category, page, optionKey);
366
+ if (!opt) {
367
+ const found = menu.findOption(optionKey);
368
+ if (found) {
369
+ opt = found.option;
370
+ category = found.category;
371
+ page = found.page;
372
+ }
373
+ }
366
374
  const maxSel = Number(opt?.maxValues);
367
375
  const multi = Number.isFinite(maxSel) && maxSel > 1;
368
376
  let saveValue = multi ? [...selected] : selected[0] ?? null;
@@ -108,6 +108,20 @@ class MenuInstance {
108
108
  return page.options.find(o => o.key === optionKey) ?? null;
109
109
  }
110
110
 
111
+ findOption(optionKey) {
112
+ for (let cat = 0; cat < this.pages.length; cat++) {
113
+ const categoryPages = this.pages[cat];
114
+ for (let page = 0; page < categoryPages.length; page++) {
115
+ const p = categoryPages[page];
116
+ const opt = p.options?.find(o => o.key === optionKey) ?? null;
117
+ if (opt) {
118
+ return { option: opt, category: cat, page };
119
+ }
120
+ }
121
+ }
122
+ return null;
123
+ }
124
+
111
125
  getNavigationTargets(category, index) {
112
126
  const categoryPages = this.pages[category];
113
127
  const hasPrevCategory = category > 0;
@@ -1 +1,83 @@
1
- const { OptionTypes } = require("../utils/types");
2
1
  if (Array.isArray(currentValue)) {
3
2
  return currentValue;
4
3
  }
5
4
  if (currentValue != null) {
6
5
  return [currentValue];
7
6
  }
8
7
  return [];
9
8
  const ctx = { userId, category, page };
10
9
  const customId = encodeId(menuKey, [opt.key, opt.type], ctx);
11
10
  const component = {
12
11
  type: opt.selectType,
13
12
  custom_id: customId,
14
13
  min_values: opt.minValues,
15
14
  max_values: opt.maxValues,
16
15
  };
17
16
  if (opt.placeholder != null) {
18
17
  component.placeholder = opt.placeholder;
19
18
  }
20
19
  if (opt.type === OptionTypes.StringSelect) {
21
20
  const selectedValues = toIdArray(currentValue);
22
21
  component.options = opt.choices.map(c => ({
23
22
  label: c.label,
24
23
  value: c.value,
25
24
  description: c.description,
26
25
  default: selectedValues.includes(c.value),
27
26
  }));
28
27
  }
29
28
  if (opt.type === OptionTypes.UserSelect) {
30
29
  const ids = toIdArray(currentValue);
31
30
  if (ids.length > 0) {
32
31
  component.default_values = ids.map(id => ({ id, type: "user" }));
33
32
  }
34
33
  }
35
34
  if (opt.type === OptionTypes.RoleSelect) {
36
35
  const ids = toIdArray(currentValue);
37
36
  if (ids.length > 0) {
38
37
  component.default_values = ids.map(id => ({ id, type: "role" }));
39
38
  }
40
39
  }
41
40
  if (opt.type === OptionTypes.ChannelSelect) {
42
41
  if (opt.channelTypes && opt.channelTypes.length > 0) {
43
42
  component.channel_types = opt.channelTypes;
44
43
  }
45
44
  const ids = toIdArray(currentValue);
46
45
  if (ids.length > 0) {
47
46
  component.default_values = ids.map(id => ({ id, type: "channel" }));
48
47
  }
49
48
  }
50
49
  return component;
51
50
  const lines = [`**${opt.title}**`];
52
51
  if (opt.description) {
53
52
  lines.push(`-# ${opt.description}`);
54
53
  }
55
54
  const textDisplay = renderTextDisplay(lines.join("\n"));
56
55
  const selectMenu = renderSelectMenuComponent(menuKey, opt, currentValue, userId, category, page);
57
56
  const actionRow = renderActionRow(selectMenu);
58
57
  return [textDisplay, actionRow, renderSeparator()];
58
+ const { OptionTypes } = require("../utils/types");
59
+ const { renderTextDisplay, renderActionRow, renderSeparator } = require("./utils");
60
+ const { encodeId } = require("../utils/customIds");
61
+
62
+ function toIdArray(currentValue) {
63
+ if (Array.isArray(currentValue)) {
64
+ return currentValue;
65
+ }
66
+ if (currentValue != null) {
67
+ return [currentValue];
68
+ }
69
+ return [];
70
+ }
71
+
72
+ function renderSelectMenuComponent(menuKey, opt, currentValue, userId, category, page) {
73
+ // Stateless: do not depend on current rendered page in the custom id.
74
+ // The handler can locate the option by key in the built menu.
75
+ const ctx = { u: userId };
76
+ const customId = encodeId(menuKey, [opt.key, opt.type], ctx);
77
+
78
+ const component = {
79
+ type: opt.selectType,
80
+ custom_id: customId,
81
+ min_values: opt.minValues,
82
+ max_values: opt.maxValues,
83
+ };
84
+
85
+ if (opt.placeholder != null) {
86
+ component.placeholder = opt.placeholder;
87
+ }
88
+
89
+ if (opt.type === OptionTypes.StringSelect) {
90
+ const selectedValues = toIdArray(currentValue);
91
+ component.options = opt.choices.map(c => ({
92
+ label: c.label,
93
+ value: c.value,
94
+ description: c.description,
95
+ default: selectedValues.includes(c.value),
96
+ }));
97
+ }
98
+
99
+ if (opt.type === OptionTypes.UserSelect) {
100
+ const ids = toIdArray(currentValue);
101
+ if (ids.length > 0) {
102
+ component.default_values = ids.map(id => ({ id, type: "user" }));
103
+ }
104
+ }
105
+
106
+ if (opt.type === OptionTypes.RoleSelect) {
107
+ const ids = toIdArray(currentValue);
108
+ if (ids.length > 0) {
109
+ component.default_values = ids.map(id => ({ id, type: "role" }));
110
+ }
111
+ }
112
+
113
+ if (opt.type === OptionTypes.ChannelSelect) {
114
+ if (opt.channelTypes && opt.channelTypes.length > 0) {
115
+ component.channel_types = opt.channelTypes;
116
+ }
117
+ const ids = toIdArray(currentValue);
118
+ if (ids.length > 0) {
119
+ component.default_values = ids.map(id => ({ id, type: "channel" }));
120
+ }
121
+ }
122
+
123
+ return component;
124
+ }
125
+
126
+ function renderSelectMenuOption(menuKey, opt, currentValue, userId, category, page) {
127
+ const lines = [`**${opt.title}**`];
128
+
129
+ if (opt.description) {
130
+ lines.push(`-# ${opt.description}`);
131
+ }
132
+
133
+ const textDisplay = renderTextDisplay(lines.join("\n"));
134
+ const selectMenu = renderSelectMenuComponent(menuKey, opt, currentValue, userId, category, page);
135
+ const actionRow = renderActionRow(selectMenu);
136
+
137
+ return [textDisplay, actionRow, renderSeparator()];
138
+ }
139
+
140
+ module.exports = { renderSelectMenuOption };