@rpg-engine/long-bow 0.8.30 → 0.8.32

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 (34) hide show
  1. package/dist/components/InformationCenter/sections/bestiary/BestiaryAdvancedFilters.d.ts +13 -0
  2. package/dist/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.d.ts +2 -2
  3. package/dist/components/InformationCenter/sections/items/ItemsAdvancedFilters.d.ts +11 -0
  4. package/dist/components/shared/AdvancedFilters/AdvancedFilters.d.ts +23 -0
  5. package/dist/components/shared/PaginatedContent/PaginatedContent.d.ts +1 -0
  6. package/dist/components/shared/SearchBar/SearchBar.d.ts +1 -0
  7. package/dist/components/shared/SearchHeader/SearchHeader.d.ts +1 -0
  8. package/dist/hooks/useTooltipPosition.d.ts +15 -0
  9. package/dist/long-bow.cjs.development.js +758 -509
  10. package/dist/long-bow.cjs.development.js.map +1 -1
  11. package/dist/long-bow.cjs.production.min.js +1 -1
  12. package/dist/long-bow.cjs.production.min.js.map +1 -1
  13. package/dist/long-bow.esm.js +665 -416
  14. package/dist/long-bow.esm.js.map +1 -1
  15. package/package.json +2 -2
  16. package/src/components/InformationCenter/InformationCenter.tsx +5 -19
  17. package/src/components/InformationCenter/sections/bestiary/BestiaryAdvancedFilters.tsx +95 -0
  18. package/src/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.tsx +124 -84
  19. package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.tsx +31 -7
  20. package/src/components/InformationCenter/sections/items/InformationCenterItemsSection.tsx +76 -78
  21. package/src/components/InformationCenter/sections/items/ItemsAdvancedFilters.tsx +80 -0
  22. package/src/components/InformationCenter/shared/BaseInformationDetails.tsx +34 -11
  23. package/src/components/Item/Cards/ItemInfo.tsx +1 -18
  24. package/src/components/Item/Inventory/ItemSlot.tsx +3 -15
  25. package/src/components/Item/Inventory/ItemSlotRenderer.tsx +2 -6
  26. package/src/components/shared/AdvancedFilters/AdvancedFilters.tsx +279 -0
  27. package/src/components/shared/Collapsible/Collapsible.tsx +1 -1
  28. package/src/components/shared/PaginatedContent/PaginatedContent.tsx +1 -0
  29. package/src/components/shared/SearchBar/SearchBar.tsx +15 -5
  30. package/src/components/shared/SearchHeader/SearchHeader.tsx +2 -0
  31. package/src/hooks/useTooltipPosition.ts +73 -0
  32. package/src/mocks/itemContainer.mocks.ts +0 -7
  33. package/dist/components/Item/Inventory/ItemSlotQuality.d.ts +0 -2
  34. package/src/components/Item/Inventory/ItemSlotQuality.ts +0 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.30",
3
+ "version": "0.8.32",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -84,7 +84,7 @@
84
84
  "dependencies": {
85
85
  "@capacitor/core": "^6.1.0",
86
86
  "@rollup/plugin-image": "^2.1.1",
87
- "@rpg-engine/shared": "^0.9.104",
87
+ "@rpg-engine/shared": "^0.10.0",
88
88
  "dayjs": "^1.11.2",
89
89
  "font-awesome": "^4.7.0",
90
90
  "fs-extra": "^10.1.0",
@@ -9,9 +9,9 @@ import {
9
9
  } from '@rpg-engine/shared';
10
10
  import { DraggableContainer } from '../DraggableContainer';
11
11
  import { InternalTabs } from '../InternalTabs/InternalTabs';
12
+ import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
12
13
  import { InformationCenterBestiarySection } from './sections/bestiary/InformationCenterBestiarySection';
13
14
  import { InformationCenterFAQSection } from './sections/faq/InformationCenterFaqSection';
14
- import { InformationCenterItemDetails } from './sections/items/InformationCenterItemDetails';
15
15
  import { InformationCenterItemsSection } from './sections/items/InformationCenterItemsSection';
16
16
  import { InformationCenterTutorialsSection } from './sections/tutorials/InformationCenterTutorialsSection';
17
17
 
@@ -47,10 +47,6 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
47
47
  initialSearchQuery = '',
48
48
  }) => {
49
49
  const [activeTab, setActiveTab] = useState('bestiary');
50
- const [
51
- selectedItem,
52
- setSelectedItem,
53
- ] = useState<IInformationCenterItem | null>(null);
54
50
 
55
51
  if (loading) {
56
52
  return <LoadingMessage>Loading...</LoadingMessage>;
@@ -117,7 +113,10 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
117
113
  ];
118
114
 
119
115
  return (
120
- <DraggableContainer title="Information Center">
116
+ <DraggableContainer
117
+ title="Information Center"
118
+ type={RPGUIContainerTypes.Framed}
119
+ >
121
120
  <Container>
122
121
  <InternalTabs
123
122
  tabs={tabs}
@@ -129,19 +128,6 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
129
128
  borderColor="#f59e0b"
130
129
  hoverColor="#fef3c7"
131
130
  />
132
- {selectedItem && (
133
- <InformationCenterItemDetails
134
- item={selectedItem}
135
- itemsAtlasJSON={itemsAtlasJSON}
136
- itemsAtlasIMG={itemsAtlasIMG}
137
- droppedBy={bestiaryItems.filter(npc =>
138
- npc.loots?.some(
139
- loot => loot.itemBlueprintKey === selectedItem.key
140
- )
141
- )}
142
- onBack={() => setSelectedItem(null)}
143
- />
144
- )}
145
131
  </Container>
146
132
  </DraggableContainer>
147
133
  );
@@ -0,0 +1,95 @@
1
+ import { EntityAttackType, NPCSubtype } from '@rpg-engine/shared';
2
+ import React from 'react';
3
+ import {
4
+ AdvancedFilters,
5
+ IFilterSection,
6
+ } from '../../../shared/AdvancedFilters/AdvancedFilters';
7
+ import { formatItemType } from '../items/InformationCenterItemsSection';
8
+
9
+ interface IBestiaryAdvancedFiltersProps {
10
+ isOpen: boolean;
11
+ onToggle: () => void;
12
+ onLevelRangeChange: (range: [number | undefined, number | undefined]) => void;
13
+ onSubtypeChange: (value: string) => void;
14
+ onAttackTypeChange: (value: string) => void;
15
+ levelRange: [number | undefined, number | undefined];
16
+ selectedSubtype: string;
17
+ selectedAttackType: string;
18
+ }
19
+
20
+ export const BestiaryAdvancedFilters = ({
21
+ isOpen,
22
+ onToggle,
23
+ onLevelRangeChange,
24
+ onSubtypeChange,
25
+ onAttackTypeChange,
26
+ levelRange,
27
+ selectedSubtype,
28
+ selectedAttackType,
29
+ }: IBestiaryAdvancedFiltersProps): JSX.Element => {
30
+ const subtypeOptions = [
31
+ { id: 0, value: 'all', option: 'All Types' },
32
+ ...Object.entries(NPCSubtype).map(([, value], index) => ({
33
+ id: index + 1,
34
+ value,
35
+ option: formatItemType(value),
36
+ })),
37
+ ];
38
+
39
+ const attackTypeOptions = [
40
+ { id: 0, value: 'all', option: 'All Attack Types' },
41
+ ...Object.entries(EntityAttackType).map(([, value], index) => ({
42
+ id: index + 1,
43
+ value,
44
+ option: formatItemType(value),
45
+ })),
46
+ ];
47
+
48
+ const hasActiveFilters =
49
+ levelRange[0] !== undefined ||
50
+ levelRange[1] !== undefined ||
51
+ selectedSubtype !== 'all' ||
52
+ selectedAttackType !== 'all';
53
+
54
+ const handleClearFilters = () => {
55
+ onLevelRangeChange([undefined, undefined]);
56
+ onSubtypeChange('all');
57
+ onAttackTypeChange('all');
58
+ };
59
+
60
+ const sections: IFilterSection[] = [
61
+ {
62
+ type: 'range',
63
+ label: 'Level Range',
64
+ key: 'level',
65
+ value: levelRange,
66
+ onChange: onLevelRangeChange,
67
+ },
68
+ {
69
+ type: 'dropdown',
70
+ label: 'Monster Type',
71
+ key: 'subtype',
72
+ options: subtypeOptions,
73
+ value: selectedSubtype,
74
+ onChange: onSubtypeChange,
75
+ },
76
+ {
77
+ type: 'dropdown',
78
+ label: 'Attack Type',
79
+ key: 'attackType',
80
+ options: attackTypeOptions,
81
+ value: selectedAttackType,
82
+ onChange: onAttackTypeChange,
83
+ },
84
+ ];
85
+
86
+ return (
87
+ <AdvancedFilters
88
+ isOpen={isOpen}
89
+ onToggle={onToggle}
90
+ sections={sections}
91
+ onClearAll={handleClearFilters}
92
+ hasActiveFilters={hasActiveFilters}
93
+ />
94
+ );
95
+ };
@@ -1,13 +1,20 @@
1
- import { IInformationCenterNPC, NPCAlignment } from '@rpg-engine/shared';
1
+ import {
2
+ IInformationCenterNPC,
3
+ NPCAlignment,
4
+ isMobileOrTablet,
5
+ } from '@rpg-engine/shared';
2
6
  import React, { useMemo, useState } from 'react';
3
7
  import styled from 'styled-components';
8
+ import { useTooltipPosition } from '../../../../hooks/useTooltipPosition';
4
9
  import { IOptionsProps } from '../../../Dropdown';
5
10
  import { PaginatedContent } from '../../../shared/PaginatedContent/PaginatedContent';
6
11
  import { Portal } from '../../../shared/Portal/Portal';
7
12
  import { InformationCenterCell } from '../../InformationCenterCell';
8
13
  import { formatItemType } from '../items/InformationCenterItemsSection';
14
+ import { BestiaryAdvancedFilters } from './BestiaryAdvancedFilters';
9
15
  import { InformationCenterNPCDetails } from './InformationCenterNPCDetails';
10
16
  import { InformationCenterNPCTooltip } from './InformationCenterNPCTooltip';
17
+
11
18
  interface IBestiarySectionProps {
12
19
  bestiaryItems: IInformationCenterNPC[];
13
20
  itemsAtlasJSON: Record<string, any>;
@@ -20,7 +27,7 @@ interface IBestiarySectionProps {
20
27
  tabId: string;
21
28
  }
22
29
 
23
- export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> = ({
30
+ export const InformationCenterBestiarySection = ({
24
31
  bestiaryItems,
25
32
  itemsAtlasJSON,
26
33
  itemsAtlasIMG,
@@ -30,83 +37,59 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
30
37
  entitiesAtlasIMG,
31
38
  initialSearchQuery,
32
39
  tabId,
33
- }) => {
34
- const [searchQuery, setSearchQuery] = useState(initialSearchQuery);
35
- const [tooltipData, setTooltipData] = useState<{
36
- npc: IInformationCenterNPC;
37
- position: { x: number; y: number };
38
- } | null>(null);
40
+ }: IBestiarySectionProps): JSX.Element => {
41
+ const isMobile = isMobileOrTablet();
42
+ const [searchQuery, setSearchQuery] = useState<string>(initialSearchQuery);
39
43
  const [
40
44
  selectedMonster,
41
45
  setSelectedMonster,
42
46
  ] = useState<IInformationCenterNPC | null>(null);
43
- const [isTouchDevice] = useState('ontouchstart' in window);
47
+ const [selectedBestiaryCategory, setSelectedBestiaryCategory] = useState<
48
+ string
49
+ >('all');
44
50
 
45
- const handleMouseEnter = (
46
- monster: IInformationCenterNPC,
47
- event: React.MouseEvent
48
- ) => {
49
- if (!isTouchDevice && !selectedMonster) {
50
- setTooltipData({
51
- npc: monster,
52
- position: { x: event.clientX, y: event.clientY },
53
- });
54
- }
55
- };
51
+ const {
52
+ tooltipState,
53
+ handleMouseEnter,
54
+ handleMouseLeave,
55
+ TOOLTIP_WIDTH,
56
+ } = useTooltipPosition<IInformationCenterNPC>();
56
57
 
57
- const handleMouseLeave = () => {
58
- if (!isTouchDevice) {
59
- setTooltipData(null);
60
- }
61
- };
62
-
63
- const handleMouseMove = (event: React.MouseEvent) => {
64
- if (!isTouchDevice && tooltipData) {
65
- setTooltipData({
66
- ...tooltipData,
67
- position: { x: event.clientX, y: event.clientY },
68
- });
69
- }
70
- };
58
+ // Advanced filters state
59
+ const [isAdvancedFiltersOpen, setIsAdvancedFiltersOpen] = useState<boolean>(
60
+ false
61
+ );
62
+ const [levelRange, setLevelRange] = useState<
63
+ [number | undefined, number | undefined]
64
+ >([undefined, undefined]);
65
+ const [selectedSubtype, setSelectedSubtype] = useState<string>('all');
66
+ const [selectedAttackType, setSelectedAttackType] = useState<string>('all');
71
67
 
72
68
  const handleTouchStart = (
73
69
  monster: IInformationCenterNPC,
74
70
  event: React.TouchEvent
75
- ) => {
76
- if (isTouchDevice) {
77
- event.preventDefault();
78
- const touch = event.touches[0];
79
- if (tooltipData?.npc.id === monster.id) {
80
- setTooltipData(null);
81
- } else {
82
- setTooltipData({
83
- npc: monster,
84
- position: { x: touch.clientX, y: touch.clientY },
85
- });
86
- }
87
- }
71
+ ): void => {
72
+ event.preventDefault();
73
+ setSelectedMonster(monster);
88
74
  };
89
75
 
90
- const handleMonsterClick = (monster: IInformationCenterNPC) => {
76
+ const handleMonsterClick = (monster: IInformationCenterNPC): void => {
91
77
  setSelectedMonster(monster);
92
- setTooltipData(null);
93
78
  };
94
79
 
95
- const [selectedBestiaryCategory, setSelectedBestiaryCategory] = useState<
96
- string
97
- >('all');
98
-
99
80
  const bestiaryCategoryOptions: IOptionsProps[] = [
100
81
  { id: 0, value: 'all', option: 'All Monsters' },
101
82
  { id: 1, value: 'bosses', option: 'Bosses' },
102
- ...Object.entries(NPCAlignment).map(([, value], index) => ({
103
- id: index + 1,
104
- value,
105
- option: formatItemType(value),
106
- })),
83
+ ...Object.entries(NPCAlignment)
84
+ .filter(([, value]) => value !== NPCAlignment.Friendly)
85
+ .map(([, value], index) => ({
86
+ id: index + 2,
87
+ value,
88
+ option: formatItemType(value),
89
+ })),
107
90
  ];
108
91
 
109
- const renderItem = (item: IInformationCenterNPC) => (
92
+ const renderItem = (item: IInformationCenterNPC): JSX.Element => (
110
93
  <InformationCenterCell
111
94
  key={item.id}
112
95
  name={item.name}
@@ -116,19 +99,19 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
116
99
  onClick={() => handleMonsterClick(item)}
117
100
  onMouseEnter={e => handleMouseEnter(item, e)}
118
101
  onMouseLeave={handleMouseLeave}
119
- onMouseMove={handleMouseMove}
120
102
  onTouchStart={e => handleTouchStart(item, e)}
121
103
  />
122
104
  );
123
105
 
124
- const filteredItems = useMemo(() => {
106
+ const filteredItems = useMemo<IInformationCenterNPC[]>(() => {
125
107
  return bestiaryItems.filter(item => {
108
+ // Basic search filter
126
109
  const matchesSearch = item.name
127
110
  .toLowerCase()
128
111
  .includes(searchQuery.toLowerCase());
129
112
 
113
+ // Category filter
130
114
  let matchesCategory = true;
131
-
132
115
  if (selectedBestiaryCategory === 'bosses') {
133
116
  matchesCategory = item.isBoss === true;
134
117
  } else if (selectedBestiaryCategory === NPCAlignment.Hostile) {
@@ -138,17 +121,60 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
138
121
  matchesCategory = item.alignment === selectedBestiaryCategory;
139
122
  }
140
123
 
141
- return matchesSearch && matchesCategory;
142
- });
143
- }, [bestiaryItems, searchQuery, selectedBestiaryCategory]);
124
+ // Advanced filters
125
+ const matchesLevel =
126
+ (!levelRange[0] || item.skills.level >= levelRange[0]) &&
127
+ (!levelRange[1] || item.skills.level <= levelRange[1]);
128
+
129
+ const matchesSubtype =
130
+ selectedSubtype === 'all' || item.subType === selectedSubtype;
131
+
132
+ const matchesAttackType =
133
+ selectedAttackType === 'all' || item.attackType === selectedAttackType;
134
+
135
+ // Filter out friendly NPCs
136
+ const isNotFriendly = item.alignment !== NPCAlignment.Friendly;
144
137
 
145
- const handleSearchChange = (newQuery: string) => {
138
+ return (
139
+ matchesSearch &&
140
+ matchesCategory &&
141
+ matchesLevel &&
142
+ matchesSubtype &&
143
+ matchesAttackType &&
144
+ isNotFriendly
145
+ );
146
+ });
147
+ }, [
148
+ bestiaryItems,
149
+ searchQuery,
150
+ selectedBestiaryCategory,
151
+ levelRange,
152
+ selectedSubtype,
153
+ selectedAttackType,
154
+ ]);
155
+
156
+ const handleSearchChange = (newQuery: string): void => {
146
157
  setSearchQuery(newQuery);
147
158
  if (newQuery && selectedBestiaryCategory !== 'all') {
148
159
  setSelectedBestiaryCategory('all');
149
160
  }
150
161
  };
151
162
 
163
+ const SearchBarRightElement = (
164
+ <SearchBarActions>
165
+ <BestiaryAdvancedFilters
166
+ isOpen={isAdvancedFiltersOpen}
167
+ onToggle={() => setIsAdvancedFiltersOpen(!isAdvancedFiltersOpen)}
168
+ onLevelRangeChange={setLevelRange}
169
+ onSubtypeChange={setSelectedSubtype}
170
+ onAttackTypeChange={setSelectedAttackType}
171
+ levelRange={levelRange}
172
+ selectedSubtype={selectedSubtype}
173
+ selectedAttackType={selectedAttackType}
174
+ />
175
+ </SearchBarActions>
176
+ );
177
+
152
178
  return (
153
179
  <>
154
180
  <PaginatedContent<IInformationCenterNPC>
@@ -166,21 +192,28 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
166
192
  value: searchQuery,
167
193
  onChange: handleSearchChange,
168
194
  placeholder: 'Search monsters...',
195
+ rightElement: SearchBarRightElement,
169
196
  }}
170
- dependencies={[selectedBestiaryCategory]}
197
+ dependencies={[
198
+ selectedBestiaryCategory,
199
+ levelRange,
200
+ selectedSubtype,
201
+ selectedAttackType,
202
+ ]}
171
203
  itemHeight="180px"
172
204
  />
173
- {tooltipData && (
205
+ {!isMobile && tooltipState && tooltipState.item && (
174
206
  <Portal>
175
207
  <TooltipWrapper
208
+ width={TOOLTIP_WIDTH}
176
209
  style={{
177
210
  position: 'fixed',
178
- left: tooltipData.position.x + 10,
179
- top: tooltipData.position.y + 10,
211
+ left: `${tooltipState.position.x}px`,
212
+ top: `${tooltipState.position.y}px`,
180
213
  }}
181
214
  >
182
215
  <InformationCenterNPCTooltip
183
- npc={tooltipData.npc}
216
+ npc={tooltipState.item}
184
217
  itemsAtlasJSON={itemsAtlasJSON}
185
218
  itemsAtlasIMG={itemsAtlasIMG}
186
219
  />
@@ -188,24 +221,31 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
188
221
  </Portal>
189
222
  )}
190
223
  {selectedMonster && (
191
- <InformationCenterNPCDetails
192
- npc={selectedMonster}
193
- itemsAtlasJSON={itemsAtlasJSON}
194
- itemsAtlasIMG={itemsAtlasIMG}
195
- iconAtlasIMG={iconsAtlasIMG}
196
- iconAtlasJSON={iconsAtlasJSON}
197
- entitiesAtlasJSON={entitiesAtlasJSON}
198
- entitiesAtlasIMG={entitiesAtlasIMG}
199
- onBack={() => setSelectedMonster(null)}
200
- />
224
+ <Portal>
225
+ <InformationCenterNPCDetails
226
+ npc={selectedMonster}
227
+ itemsAtlasJSON={itemsAtlasJSON}
228
+ itemsAtlasIMG={itemsAtlasIMG}
229
+ iconAtlasIMG={iconsAtlasIMG}
230
+ iconAtlasJSON={iconsAtlasJSON}
231
+ entitiesAtlasJSON={entitiesAtlasJSON}
232
+ entitiesAtlasIMG={entitiesAtlasIMG}
233
+ onBack={() => setSelectedMonster(null)}
234
+ />
235
+ </Portal>
201
236
  )}
202
237
  </>
203
238
  );
204
239
  };
205
240
 
206
- const TooltipWrapper = styled.div`
207
- position: fixed;
241
+ const TooltipWrapper = styled.div<{ width: number }>`
208
242
  z-index: 1000;
209
243
  pointer-events: none;
210
- width: 300px;
244
+ width: ${props => `${props.width}px`};
245
+ `;
246
+
247
+ const SearchBarActions = styled.div`
248
+ display: flex;
249
+ align-items: center;
250
+ gap: 0.5rem;
211
251
  `;
@@ -265,9 +265,11 @@ const Value = styled.span`
265
265
  const StyledCollapsible = styled(Collapsible)`
266
266
  background: rgba(255, 255, 255, 0.05);
267
267
  border-radius: 4px;
268
- overflow: auto;
268
+ overflow: visible;
269
269
  scrollbar-width: thin;
270
270
  scrollbar-color: ${uiColors.darkGray} transparent;
271
+ width: 100%;
272
+ box-sizing: border-box;
271
273
  `;
272
274
 
273
275
  const StatGrid = styled.div`
@@ -354,27 +356,49 @@ const Separator = styled.span`
354
356
  `;
355
357
 
356
358
  const LootSearchContainer = styled.div`
357
- padding: 12px 12px 0;
359
+ padding: 8px;
360
+ background: rgba(0, 0, 0, 0.2);
361
+ border-radius: 4px;
362
+ margin: 8px 8px 4px;
363
+ box-sizing: border-box;
358
364
  `;
359
365
 
360
366
  const StyledSearchBar = styled(SearchBar)`
361
367
  width: 100%;
368
+ box-sizing: border-box;
369
+ input {
370
+ background: rgba(0, 0, 0, 0.2) !important;
371
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
372
+ box-sizing: border-box;
373
+ &:focus {
374
+ border-color: ${uiColors.yellow} !important;
375
+ }
376
+ }
362
377
  `;
363
378
 
364
379
  const LootGrid = styled.div`
365
380
  display: grid;
366
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
367
- gap: 8px;
368
- padding: 12px;
381
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
382
+ gap: 4px;
383
+ padding: 4px 8px;
384
+ width: 100%;
385
+ max-width: 100%;
386
+ box-sizing: border-box;
387
+
388
+ @media (max-width: 768px) {
389
+ grid-template-columns: 1fr;
390
+ padding: 4px;
391
+ }
369
392
  `;
370
393
 
371
394
  const LootItem = styled.div`
372
395
  display: flex;
373
396
  align-items: center;
374
- gap: 8px;
397
+ gap: 4px;
375
398
  background: rgba(255, 255, 255, 0.05);
376
- padding: 8px;
399
+ padding: 4px;
377
400
  border-radius: 4px;
401
+ min-width: 0;
378
402
  `;
379
403
 
380
404
  const LootDetails = styled.div`