@rpg-engine/long-bow 0.8.21 → 0.8.23

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.
Files changed (33) hide show
  1. package/dist/components/InformationCenter/InformationCenter.d.ts +16 -13
  2. package/dist/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.d.ts +1 -1
  3. package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.d.ts +2 -1
  4. package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.d.ts +1 -1
  5. package/dist/components/InformationCenter/sections/faq/InformationCenterFaqSection.d.ts +1 -1
  6. package/dist/components/InformationCenter/sections/items/InformationCenterItemDetails.d.ts +1 -1
  7. package/dist/components/InformationCenter/sections/items/InformationCenterItemTooltip.d.ts +1 -1
  8. package/dist/components/InformationCenter/sections/items/InformationCenterItemsSection.d.ts +2 -1
  9. package/dist/components/InformationCenter/sections/tutorials/InformationCenterTutorialsSection.d.ts +1 -1
  10. package/dist/long-bow.cjs.development.js +795 -688
  11. package/dist/long-bow.cjs.development.js.map +1 -1
  12. package/dist/long-bow.cjs.production.min.js +1 -1
  13. package/dist/long-bow.cjs.production.min.js.map +1 -1
  14. package/dist/long-bow.esm.js +796 -689
  15. package/dist/long-bow.esm.js.map +1 -1
  16. package/dist/mocks/informationCenter.mocks.d.ts +1 -2
  17. package/package.json +2 -2
  18. package/src/components/CraftBook/CraftBook.tsx +1 -1
  19. package/src/components/Dropdown.tsx +25 -3
  20. package/src/components/InformationCenter/InformationCenter.tsx +125 -124
  21. package/src/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.tsx +52 -7
  22. package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.tsx +201 -207
  23. package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.tsx +7 -6
  24. package/src/components/InformationCenter/sections/faq/InformationCenterFaqSection.tsx +1 -1
  25. package/src/components/InformationCenter/sections/items/InformationCenterItemDetails.tsx +8 -6
  26. package/src/components/InformationCenter/sections/items/InformationCenterItemTooltip.tsx +1 -1
  27. package/src/components/InformationCenter/sections/items/InformationCenterItemsSection.tsx +36 -10
  28. package/src/components/InformationCenter/sections/tutorials/InformationCenterTutorialsSection.tsx +146 -36
  29. package/src/components/InformationCenter/shared/BaseInformationDetails.tsx +2 -0
  30. package/src/mocks/informationCenter.mocks.ts +159 -48
  31. package/src/stories/UI/info/InformationCenter.stories.tsx +1 -1
  32. package/dist/components/InformationCenter/InformationCenterTypes.d.ts +0 -96
  33. package/src/components/InformationCenter/InformationCenterTypes.ts +0 -105
@@ -1,4 +1,9 @@
1
- import { isMobileOrTablet, RangeTypes } from '@rpg-engine/shared';
1
+ import {
2
+ IInformationCenterNPC,
3
+ isMobileOrTablet,
4
+ MovementSpeed,
5
+ RangeTypes,
6
+ } from '@rpg-engine/shared';
2
7
  import React, { useState } from 'react';
3
8
  import styled from 'styled-components';
4
9
  import { uiColors } from '../../../../constants/uiColors';
@@ -6,10 +11,7 @@ import { Collapsible } from '../../../shared/Collapsible/Collapsible';
6
11
  import { Pagination } from '../../../shared/Pagination/Pagination';
7
12
  import { SearchBar } from '../../../shared/SearchBar/SearchBar';
8
13
  import { SpriteFromAtlas } from '../../../shared/SpriteFromAtlas';
9
- import {
10
- IInformationCenterNPC,
11
- MovementSpeed,
12
- } from '../../InformationCenterTypes';
14
+
13
15
  import { BaseInformationDetails } from '../../shared/BaseInformationDetails';
14
16
 
15
17
  interface INPCDetailsProps {
@@ -25,7 +27,7 @@ interface INPCDetailsProps {
25
27
 
26
28
  const ITEMS_PER_PAGE = 5;
27
29
 
28
- const formatItemName = (itemPath: string): string => {
30
+ export const formatItemName = (itemPath: string): string => {
29
31
  const cleanText =
30
32
  itemPath
31
33
  .split('/')
@@ -40,207 +42,199 @@ const formatItemName = (itemPath: string): string => {
40
42
  };
41
43
 
42
44
  export const InformationCenterNPCDetails: React.FC<INPCDetailsProps> = ({
43
- npc,
44
- itemsAtlasJSON,
45
- itemsAtlasIMG,
46
- entitiesAtlasJSON,
47
- entitiesAtlasIMG,
48
- iconAtlasIMG,
49
- iconAtlasJSON,
50
- onBack,
51
- }) => {
52
- const isMobile = isMobileOrTablet();
53
- const [lootSearchQuery, setLootSearchQuery] = useState('');
54
- const [currentLootPage, setCurrentLootPage] = useState(1);
55
-
56
- const formatText = (
57
- text: string | RangeTypes | MovementSpeed
58
- ): string => {
59
- if (typeof text === 'number') {
60
- return text.toString();
61
- }
62
-
63
- return text
64
- .toString()
65
- .replace(/([A-Z])/g, ' $1')
66
- .trim()
67
- .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
68
- .replace(/\s+/g, ' ');
69
- };
70
-
71
- const formatRarity = (rarity: number): string => {
72
- switch (rarity) {
73
- case 0.5:
74
- return 'Very Rare';
75
- case 1:
76
- return 'Rare';
77
- case 10:
78
- return 'Uncommon';
79
- case 15:
80
- return 'Semi Common';
81
- case 20:
82
- return 'Common';
83
- case 35:
84
- return 'Very Common';
85
- default:
86
- return 'Unknown';
87
- }
88
- };
89
-
90
- const filteredLoots =
91
- npc.loots?.filter(loot =>
92
- formatItemName(loot.itemBlueprintKey)
93
- .toLowerCase()
94
- .includes(lootSearchQuery.toLowerCase())
95
- ) || [];
96
-
97
- const totalLootPages = Math.ceil(
98
- filteredLoots.length / ITEMS_PER_PAGE
99
- );
100
- const paginatedLoots = filteredLoots.slice(
101
- (currentLootPage - 1) * ITEMS_PER_PAGE,
102
- currentLootPage * ITEMS_PER_PAGE
103
- );
104
-
105
- return (
106
- <BaseInformationDetails
107
- name={npc.name}
108
- spriteKey={npc.key}
109
- atlasJSON={entitiesAtlasJSON}
110
- atlasIMG={entitiesAtlasIMG}
111
- onBack={onBack}
112
- >
113
- <InfoSection>
114
- <InfoItem>
115
- <Label>Type:</Label>
116
- <Value>{formatText(npc.subType)}</Value>
117
- </InfoItem>
118
- <InfoItem>
119
- <Label>Alignment:</Label>
120
- <Value>{formatText(npc.alignment)}</Value>
121
- </InfoItem>
122
- <InfoItem>
123
- <Label>Attack Type:</Label>
124
- <Value>{formatText(npc.attackType)}</Value>
125
- </InfoItem>
126
- <InfoItem>
127
- <Label>Range:</Label>
128
- <Value>{formatText(npc.maxRangeAttack)}</Value>
129
- </InfoItem>
130
- <InfoItem>
131
- <Label>Speed:</Label>
132
- <Value>{formatText(npc.speed)}</Value>
133
- </InfoItem>
134
- </InfoSection>
135
-
136
- <StyledCollapsible title="Stats" defaultOpen={!isMobile}>
137
- <StatGrid>
138
- <StatItem>HP: {npc.baseHealth}</StatItem>
139
- <StatItem>Level: {npc.skills.level}</StatItem>
140
- {npc.skills.strength?.level && (
141
- <StatItem>Strength: {npc.skills.strength.level}</StatItem>
142
- )}
143
- {npc.skills.dexterity?.level && (
144
- <StatItem>Dexterity: {npc.skills.dexterity.level}</StatItem>
145
- )}
146
- {npc.skills.resistance?.level && (
147
- <StatItem>
148
- Resistance: {npc.skills.resistance.level}
149
- </StatItem>
150
- )}
151
- </StatGrid>
152
- </StyledCollapsible>
153
-
154
- {npc.loots && npc.loots.length > 0 && (
155
- <StyledCollapsible title="Loot" defaultOpen={!isMobile}>
156
- <LootSearchContainer>
157
- <StyledSearchBar
158
- value={lootSearchQuery}
159
- onChange={setLootSearchQuery}
160
- placeholder="Search loot..."
161
- />
162
- </LootSearchContainer>
163
- <LootGrid>
164
- {paginatedLoots.map((loot, index) => (
165
- <LootItem key={index}>
166
- <SpriteFromAtlas
167
- atlasJSON={itemsAtlasJSON}
168
- atlasIMG={itemsAtlasIMG}
169
- spriteKey={loot.itemBlueprintKey}
170
- width={24}
171
- height={24}
172
- imgScale={1}
173
- />
174
- <LootDetails>
175
- <LootName>
176
- {formatItemName(loot.itemBlueprintKey)}
177
- </LootName>
178
- <LootInfo>
179
- <LootChance>{formatRarity(loot.chance)}</LootChance>
180
- {loot.quantityRange && (
181
- <LootQuantity>
182
- x{loot.quantityRange[0]}-{loot.quantityRange[1]}
183
- </LootQuantity>
184
- )}
185
- </LootInfo>
186
- </LootDetails>
187
- </LootItem>
188
- ))}
189
- </LootGrid>
190
- {filteredLoots.length > ITEMS_PER_PAGE && (
191
- <PaginationContainer>
192
- <StyledPagination
193
- currentPage={currentLootPage}
194
- totalPages={totalLootPages}
195
- onPageChange={setCurrentLootPage}
196
- />
197
- </PaginationContainer>
198
- )}
199
- </StyledCollapsible>
200
- )}
201
-
202
- {npc.entityEffects && npc.entityEffects.length > 0 && (
203
- <StyledCollapsible title="Effects" defaultOpen={!isMobile}>
204
- <EffectsList>
205
- {npc.entityEffects.map((effect, index) => (
206
- <EffectItem key={index}>{formatText(effect)}</EffectItem>
207
- ))}
208
- </EffectsList>
209
- </StyledCollapsible>
210
- )}
211
-
212
- {npc.areaSpells && npc.areaSpells.length > 0 && (
213
- <StyledCollapsible title="Spells" defaultOpen={!isMobile}>
214
- <SpellsList>
215
- {npc.areaSpells.map((spell, index) => (
216
- <SpellItem key={index}>
217
- <SpellIconContainer>
218
- <SpriteFromAtlas
219
- atlasJSON={iconAtlasJSON}
220
- atlasIMG={iconAtlasIMG}
221
- spriteKey={spell.texturePath || spell.spellKey}
222
- width={24}
223
- height={24}
224
- imgScale={1}
225
- />
226
- </SpellIconContainer>
227
- <SpellContent>
228
- <SpellName>{formatText(spell.spellKey)}</SpellName>
229
- <SpellDetails>
230
- Power:{' '}
231
- <SpellValue>{formatText(spell.power)}</SpellValue>
232
- <Separator>•</Separator>
233
- Chance: <SpellValue>{spell.probability}%</SpellValue>
234
- </SpellDetails>
235
- </SpellContent>
236
- </SpellItem>
237
- ))}
238
- </SpellsList>
239
- </StyledCollapsible>
240
- )}
241
- </BaseInformationDetails>
242
- );
243
- };
45
+ npc,
46
+ itemsAtlasJSON,
47
+ itemsAtlasIMG,
48
+ entitiesAtlasJSON,
49
+ entitiesAtlasIMG,
50
+ iconAtlasIMG,
51
+ iconAtlasJSON,
52
+ onBack,
53
+ }) => {
54
+ const isMobile = isMobileOrTablet();
55
+ const [lootSearchQuery, setLootSearchQuery] = useState('');
56
+ const [currentLootPage, setCurrentLootPage] = useState(1);
57
+
58
+ const formatText = (text: string | RangeTypes | MovementSpeed): string => {
59
+ if (typeof text === 'number') {
60
+ return text.toString();
61
+ }
62
+
63
+ return text
64
+ .toString()
65
+ .replace(/([A-Z])/g, ' $1')
66
+ .trim()
67
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
68
+ .replace(/\s+/g, ' ')
69
+ .replace(/^\w/, c => c.toUpperCase());
70
+ };
71
+
72
+ const formatRarity = (rarity: number): string => {
73
+ switch (rarity) {
74
+ case 0.5:
75
+ return 'Very Rare';
76
+ case 1:
77
+ return 'Rare';
78
+ case 10:
79
+ return 'Uncommon';
80
+ case 15:
81
+ return 'Semi Common';
82
+ case 20:
83
+ return 'Common';
84
+ case 35:
85
+ return 'Very Common';
86
+ default:
87
+ return 'Unknown';
88
+ }
89
+ };
90
+
91
+ const filteredLoots =
92
+ npc.loots?.filter(loot =>
93
+ formatItemName(loot.itemBlueprintKey)
94
+ .toLowerCase()
95
+ .includes(lootSearchQuery.toLowerCase())
96
+ ) || [];
97
+
98
+ const totalLootPages = Math.ceil(filteredLoots.length / ITEMS_PER_PAGE);
99
+ const paginatedLoots = filteredLoots.slice(
100
+ (currentLootPage - 1) * ITEMS_PER_PAGE,
101
+ currentLootPage * ITEMS_PER_PAGE
102
+ );
103
+
104
+ return (
105
+ <BaseInformationDetails
106
+ name={npc.name}
107
+ spriteKey={npc.key}
108
+ atlasJSON={entitiesAtlasJSON}
109
+ atlasIMG={entitiesAtlasIMG}
110
+ onBack={onBack}
111
+ >
112
+ <InfoSection>
113
+ <InfoItem>
114
+ <Label>Type:</Label>
115
+ <Value>{formatText(npc.subType)}</Value>
116
+ </InfoItem>
117
+ <InfoItem>
118
+ <Label>Alignment:</Label>
119
+ <Value>{formatText(npc.alignment)}</Value>
120
+ </InfoItem>
121
+ <InfoItem>
122
+ <Label>Attack Type:</Label>
123
+ <Value>{formatText(npc.attackType)}</Value>
124
+ </InfoItem>
125
+ <InfoItem>
126
+ <Label>Range:</Label>
127
+ <Value>{formatText(npc.maxRangeAttack)}</Value>
128
+ </InfoItem>
129
+ <InfoItem>
130
+ <Label>Speed:</Label>
131
+ <Value>{formatText(npc.speed)}</Value>
132
+ </InfoItem>
133
+ </InfoSection>
134
+
135
+ <StyledCollapsible title="Stats" defaultOpen={!isMobile}>
136
+ <StatGrid>
137
+ <StatItem>HP: {npc.baseHealth}</StatItem>
138
+ <StatItem>Level: {npc.skills.level}</StatItem>
139
+ {npc.skills.strength?.level && (
140
+ <StatItem>Strength: {npc.skills.strength.level}</StatItem>
141
+ )}
142
+ {npc.skills.dexterity?.level && (
143
+ <StatItem>Dexterity: {npc.skills.dexterity.level}</StatItem>
144
+ )}
145
+ {npc.skills.resistance?.level && (
146
+ <StatItem>Resistance: {npc.skills.resistance.level}</StatItem>
147
+ )}
148
+ </StatGrid>
149
+ </StyledCollapsible>
150
+
151
+ {npc.loots && npc.loots.length > 0 && (
152
+ <StyledCollapsible title="Loot" defaultOpen={!isMobile}>
153
+ <LootSearchContainer>
154
+ <StyledSearchBar
155
+ value={lootSearchQuery}
156
+ onChange={setLootSearchQuery}
157
+ placeholder="Search loot..."
158
+ />
159
+ </LootSearchContainer>
160
+ <LootGrid>
161
+ {paginatedLoots.map((loot, index) => (
162
+ <LootItem key={index}>
163
+ <SpriteFromAtlas
164
+ atlasJSON={itemsAtlasJSON}
165
+ atlasIMG={itemsAtlasIMG}
166
+ spriteKey={loot.itemBlueprintKey}
167
+ width={24}
168
+ height={24}
169
+ imgScale={1}
170
+ />
171
+ <LootDetails>
172
+ <LootName>{formatItemName(loot.itemBlueprintKey)}</LootName>
173
+ <LootInfo>
174
+ <LootChance>{formatRarity(loot.chance)}</LootChance>
175
+ {loot.quantityRange && (
176
+ <LootQuantity>
177
+ x{loot.quantityRange[0]}-{loot.quantityRange[1]}
178
+ </LootQuantity>
179
+ )}
180
+ </LootInfo>
181
+ </LootDetails>
182
+ </LootItem>
183
+ ))}
184
+ </LootGrid>
185
+ {filteredLoots.length > ITEMS_PER_PAGE && (
186
+ <PaginationContainer>
187
+ <StyledPagination
188
+ currentPage={currentLootPage}
189
+ totalPages={totalLootPages}
190
+ onPageChange={setCurrentLootPage}
191
+ />
192
+ </PaginationContainer>
193
+ )}
194
+ </StyledCollapsible>
195
+ )}
196
+
197
+ {npc.entityEffects && npc.entityEffects.length > 0 && (
198
+ <StyledCollapsible title="Effects" defaultOpen={!isMobile}>
199
+ <EffectsList>
200
+ {npc.entityEffects.map((effect, index) => (
201
+ <EffectItem key={index}>{formatText(effect)}</EffectItem>
202
+ ))}
203
+ </EffectsList>
204
+ </StyledCollapsible>
205
+ )}
206
+
207
+ {npc.areaSpells && npc.areaSpells.length > 0 && (
208
+ <StyledCollapsible title="Spells" defaultOpen={!isMobile}>
209
+ <SpellsList>
210
+ {npc.areaSpells.map((spell, index) => (
211
+ <SpellItem key={index}>
212
+ <SpellIconContainer>
213
+ <SpriteFromAtlas
214
+ atlasJSON={iconAtlasJSON}
215
+ atlasIMG={iconAtlasIMG}
216
+ spriteKey={spell.texturePath || spell.spellKey}
217
+ width={24}
218
+ height={24}
219
+ imgScale={1}
220
+ />
221
+ </SpellIconContainer>
222
+ <SpellContent>
223
+ <SpellName>{formatText(spell.spellKey)}</SpellName>
224
+ <SpellDetails>
225
+ Power: <SpellValue>{formatText(spell.power)}</SpellValue>
226
+ <Separator>•</Separator>
227
+ Chance: <SpellValue>{spell.probability}%</SpellValue>
228
+ </SpellDetails>
229
+ </SpellContent>
230
+ </SpellItem>
231
+ ))}
232
+ </SpellsList>
233
+ </StyledCollapsible>
234
+ )}
235
+ </BaseInformationDetails>
236
+ );
237
+ };
244
238
 
245
239
  const InfoSection = styled.div`
246
240
  display: grid;
@@ -1,4 +1,8 @@
1
- import { RangeTypes } from '@rpg-engine/shared';
1
+ import {
2
+ IInformationCenterNPC,
3
+ MovementSpeed,
4
+ RangeTypes,
5
+ } from '@rpg-engine/shared';
2
6
  import React from 'react';
3
7
  import styled from 'styled-components';
4
8
  import { uiColors } from '../../../../constants/uiColors';
@@ -10,10 +14,7 @@ import BaseTooltip, {
10
14
  TooltipTitle,
11
15
  } from '../../../shared/BaseTooltip';
12
16
  import { SpriteFromAtlas } from '../../../shared/SpriteFromAtlas';
13
- import {
14
- IInformationCenterNPC,
15
- MovementSpeed,
16
- } from '../../InformationCenterTypes';
17
+ import { formatItemName } from './InformationCenterNPCDetails';
17
18
 
18
19
  interface INPCTooltipProps {
19
20
  npc: IInformationCenterNPC;
@@ -191,7 +192,7 @@ export const InformationCenterNPCTooltip: React.FC<INPCTooltipProps> = ({
191
192
  spriteKey={loot.itemBlueprintKey}
192
193
  imgScale={1}
193
194
  />
194
- <LootName>{formatText(loot.itemBlueprintKey)}</LootName>
195
+ <LootName>{formatItemName(loot.itemBlueprintKey)}</LootName>
195
196
  <LootChance>{formatRarity(loot.chance)}</LootChance>
196
197
  </LootItem>
197
198
  ))}
@@ -1,9 +1,9 @@
1
+ import { IFaqItem } from '@rpg-engine/shared';
1
2
  import React, { useEffect, useMemo, useState } from 'react';
2
3
  import styled from 'styled-components';
3
4
  import { Collapsible } from '../../../shared/Collapsible/Collapsible';
4
5
  import { PaginatedContent } from '../../../shared/PaginatedContent/PaginatedContent';
5
6
  import { SearchHeader } from '../../../shared/SearchHeader/SearchHeader';
6
- import { IFaqItem } from '../../InformationCenter';
7
7
 
8
8
  interface IFaqSectionProps {
9
9
  faqItems: IFaqItem[];
@@ -1,12 +1,12 @@
1
- import { isMobileOrTablet } from '@rpg-engine/shared';
1
+ import {
2
+ IInformationCenterItem,
3
+ IInformationCenterNPC,
4
+ isMobileOrTablet,
5
+ } from '@rpg-engine/shared';
2
6
  import React from 'react';
3
7
  import styled from 'styled-components';
4
8
  import { uiColors } from '../../../../constants/uiColors';
5
9
  import { Collapsible } from '../../../shared/Collapsible/Collapsible';
6
- import {
7
- IInformationCenterItem,
8
- IInformationCenterNPC,
9
- } from '../../InformationCenterTypes';
10
10
  import { BaseInformationDetails } from '../../shared/BaseInformationDetails';
11
11
 
12
12
  interface IInformationCenterItemDetailsProps {
@@ -130,7 +130,9 @@ export const InformationCenterItemDetails: React.FC<IInformationCenterItemDetail
130
130
  <BuffsList>
131
131
  {item.equippedBuff.map((buff, index) => (
132
132
  <BuffItem key={index}>
133
- <BuffName>{buff.trait}</BuffName>
133
+ <BuffName>
134
+ {buff.trait.replace(/^\w/, c => c.toUpperCase())}
135
+ </BuffName>
134
136
  <BuffValue>+{buff.buffPercentage}%</BuffValue>
135
137
  </BuffItem>
136
138
  ))}
@@ -1,3 +1,4 @@
1
+ import { IInformationCenterItem } from '@rpg-engine/shared';
1
2
  import React from 'react';
2
3
  import styled from 'styled-components';
3
4
  import { uiColors } from '../../../../constants/uiColors';
@@ -8,7 +9,6 @@ import BaseTooltip, {
8
9
  StatsContainer,
9
10
  TooltipTitle,
10
11
  } from '../../../shared/BaseTooltip';
11
- import { IInformationCenterItem } from '../../InformationCenterTypes';
12
12
 
13
13
  interface IItemTooltipProps {
14
14
  item: IInformationCenterItem;
@@ -1,17 +1,17 @@
1
- import { ItemType } from '@rpg-engine/shared';
1
+ import {
2
+ IInformationCenterItem,
3
+ IInformationCenterNPC,
4
+ ItemType,
5
+ } from '@rpg-engine/shared';
2
6
  import React, { useState } from 'react';
3
7
  import styled from 'styled-components';
4
8
  import { IOptionsProps } from '../../../Dropdown';
5
9
  import { PaginatedContent } from '../../../shared/PaginatedContent/PaginatedContent';
6
10
  import { InformationCenterCell } from '../../InformationCenterCell';
7
- import {
8
- IInformationCenterItem,
9
- IInformationCenterNPC,
10
- } from '../../InformationCenterTypes';
11
11
  import { InformationCenterItemDetails } from './InformationCenterItemDetails';
12
12
  import { InformationCenterItemTooltip } from './InformationCenterItemTooltip';
13
13
 
14
- const TOOLTIP_OFFSET = 20;
14
+ const TOOLTIP_OFFSET = 200;
15
15
 
16
16
  interface IItemsSectionProps {
17
17
  items: IInformationCenterItem[];
@@ -22,6 +22,23 @@ interface IItemsSectionProps {
22
22
  tabId: string;
23
23
  }
24
24
 
25
+ export const formatItemType = (type: string): string => {
26
+ const words = type.split(/(?=[A-Z])/);
27
+
28
+ if (words.length === 1) {
29
+ return words[0];
30
+ }
31
+
32
+ return words
33
+ .map((word, index) => {
34
+ if (index === 0) {
35
+ return word.charAt(0) + '.';
36
+ }
37
+ return word;
38
+ })
39
+ .join(' ');
40
+ };
41
+
25
42
  export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
26
43
  items,
27
44
  bestiaryItems,
@@ -45,9 +62,11 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
45
62
 
46
63
  const itemCategoryOptions: IOptionsProps[] = [
47
64
  { id: 0, value: 'all', option: 'All Items' },
48
- { id: 1, value: ItemType.Consumable, option: 'Consumables' },
49
- { id: 2, value: ItemType.Weapon, option: 'Weapons' },
50
- { id: 3, value: ItemType.Armor, option: 'Armor' },
65
+ ...Object.values(ItemType).map((type, index) => ({
66
+ id: index + 1,
67
+ value: type,
68
+ option: formatItemType(type),
69
+ })),
51
70
  ];
52
71
 
53
72
  const filteredItems = items.filter(
@@ -108,6 +127,13 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
108
127
  setHoveredItem(null);
109
128
  };
110
129
 
130
+ const handleSearchChange = (newQuery: string) => {
131
+ setSearchQuery(newQuery);
132
+ if (newQuery && selectedItemCategory !== 'all') {
133
+ setSelectedItemCategory('all');
134
+ }
135
+ };
136
+
111
137
  const renderItem = (item: IInformationCenterItem) => (
112
138
  <InformationCenterCell
113
139
  key={item.key}
@@ -136,7 +162,7 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
136
162
  }}
137
163
  searchOptions={{
138
164
  value: searchQuery,
139
- onChange: setSearchQuery,
165
+ onChange: handleSearchChange,
140
166
  placeholder: 'Search items...',
141
167
  }}
142
168
  dependencies={[selectedItemCategory]}