@rpg-engine/long-bow 0.7.97 → 0.7.98
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/dist/components/DPad/JoystickDPad.d.ts +21 -0
- package/dist/components/InformationCenter/InformationCenter.d.ts +29 -0
- package/dist/components/InformationCenter/InformationCenterCell.d.ts +14 -0
- package/dist/components/InformationCenter/InformationCenterTabView.d.ts +19 -0
- package/dist/components/InformationCenter/InformationCenterTypes.d.ts +79 -0
- package/dist/components/InformationCenter/sections/bestiary/BestiarySection.d.ts +12 -0
- package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.d.ts +12 -0
- package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.d.ts +9 -0
- package/dist/components/InformationCenter/sections/faq/FaqSection.d.ts +8 -0
- package/dist/components/InformationCenter/sections/items/InformationCenterItemDetails.d.ts +11 -0
- package/dist/components/InformationCenter/sections/items/InformationCenterItemTooltip.d.ts +7 -0
- package/dist/components/InformationCenter/sections/items/ItemsSection.d.ts +11 -0
- package/dist/components/InformationCenter/sections/tutorials/TutorialsSection.d.ts +8 -0
- package/dist/components/InformationCenter/shared/BaseInformationDetails.d.ts +10 -0
- package/dist/components/shared/BaseTooltip.d.ts +12 -0
- package/dist/components/shared/Collapsible/Collapsible.d.ts +9 -0
- package/dist/components/shared/Portal/Portal.d.ts +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +243 -5
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +244 -7
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/mocks/informationCenter.mocks.d.ts +6 -0
- package/dist/stories/Features/craftbook/CraftBook.stories.d.ts +2 -0
- package/dist/stories/UI/info/InformationCenter.stories.d.ts +7 -0
- package/dist/stories/UI/joystick/JoystickDPad.stories.d.ts +6 -0
- package/package.json +1 -1
- package/src/components/CraftBook/CraftBook.tsx +70 -31
- package/src/components/DPad/JoystickDPad.tsx +318 -0
- package/src/components/InformationCenter/InformationCenter.tsx +155 -0
- package/src/components/InformationCenter/InformationCenterCell.tsx +96 -0
- package/src/components/InformationCenter/InformationCenterTabView.tsx +121 -0
- package/src/components/InformationCenter/InformationCenterTypes.ts +87 -0
- package/src/components/InformationCenter/sections/bestiary/BestiarySection.tsx +170 -0
- package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.tsx +366 -0
- package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.tsx +204 -0
- package/src/components/InformationCenter/sections/faq/FaqSection.tsx +71 -0
- package/src/components/InformationCenter/sections/items/InformationCenterItemDetails.tsx +323 -0
- package/src/components/InformationCenter/sections/items/InformationCenterItemTooltip.tsx +88 -0
- package/src/components/InformationCenter/sections/items/ItemsSection.tsx +180 -0
- package/src/components/InformationCenter/sections/tutorials/TutorialsSection.tsx +144 -0
- package/src/components/InformationCenter/shared/BaseInformationDetails.tsx +162 -0
- package/src/components/InternalTabs/InternalTabs.tsx +1 -3
- package/src/components/shared/BaseTooltip.tsx +60 -0
- package/src/components/shared/Collapsible/Collapsible.tsx +70 -0
- package/src/components/shared/Portal/Portal.tsx +19 -0
- package/src/index.tsx +1 -0
- package/src/mocks/informationCenter.mocks.ts +562 -0
- package/src/stories/Features/craftbook/CraftBook.stories.tsx +15 -1
- package/src/stories/UI/info/InformationCenter.stories.tsx +58 -0
- package/src/stories/UI/joystick/JoystickDPad.stories.tsx +52 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
4
|
+
|
|
5
|
+
interface IInformationCenterCellProps {
|
|
6
|
+
name: string;
|
|
7
|
+
spriteKey: string;
|
|
8
|
+
atlasJSON: any;
|
|
9
|
+
atlasIMG: string;
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
onMouseEnter?: (e: React.MouseEvent) => void;
|
|
12
|
+
onMouseLeave?: () => void;
|
|
13
|
+
onMouseMove?: (e: React.MouseEvent) => void;
|
|
14
|
+
onTouchStart?: (e: React.TouchEvent) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const InformationCenterCell: React.FC<IInformationCenterCellProps> = ({
|
|
18
|
+
name,
|
|
19
|
+
spriteKey,
|
|
20
|
+
atlasJSON,
|
|
21
|
+
atlasIMG,
|
|
22
|
+
onClick,
|
|
23
|
+
onMouseEnter,
|
|
24
|
+
onMouseLeave,
|
|
25
|
+
onMouseMove,
|
|
26
|
+
onTouchStart,
|
|
27
|
+
}) => {
|
|
28
|
+
return (
|
|
29
|
+
<CellContainer
|
|
30
|
+
onClick={onClick}
|
|
31
|
+
onMouseEnter={onMouseEnter}
|
|
32
|
+
onMouseLeave={onMouseLeave}
|
|
33
|
+
onMouseMove={onMouseMove}
|
|
34
|
+
onTouchStart={onTouchStart}
|
|
35
|
+
>
|
|
36
|
+
<SpriteContainer>
|
|
37
|
+
<SpriteFromAtlas
|
|
38
|
+
atlasJSON={atlasJSON}
|
|
39
|
+
atlasIMG={atlasIMG}
|
|
40
|
+
spriteKey={spriteKey}
|
|
41
|
+
width={32}
|
|
42
|
+
height={32}
|
|
43
|
+
imgScale={1}
|
|
44
|
+
/>
|
|
45
|
+
</SpriteContainer>
|
|
46
|
+
<CellName>{name}</CellName>
|
|
47
|
+
</CellContainer>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const CellContainer = styled.div`
|
|
52
|
+
position: relative;
|
|
53
|
+
background: rgba(0, 0, 0, 0.2);
|
|
54
|
+
padding: 0.75rem;
|
|
55
|
+
border-radius: 4px;
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
align-items: center;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
transition: background-color 0.2s ease;
|
|
61
|
+
min-height: 120px;
|
|
62
|
+
|
|
63
|
+
&:hover {
|
|
64
|
+
background: rgba(0, 0, 0, 0.3);
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const SpriteContainer = styled.div`
|
|
69
|
+
margin-bottom: 0.5rem;
|
|
70
|
+
display: flex;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
align-items: center;
|
|
73
|
+
width: 64px;
|
|
74
|
+
height: 64px;
|
|
75
|
+
position: relative;
|
|
76
|
+
background: rgba(0, 0, 0, 0.3);
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
|
|
79
|
+
.sprite-from-atlas-img {
|
|
80
|
+
position: absolute;
|
|
81
|
+
top: 50%;
|
|
82
|
+
left: 50%;
|
|
83
|
+
transform: translate(-50%, -50%) scale(2);
|
|
84
|
+
image-rendering: pixelated;
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
const CellName = styled.h3`
|
|
89
|
+
font-size: 0.7rem;
|
|
90
|
+
color: #fef08a;
|
|
91
|
+
margin: 0;
|
|
92
|
+
text-align: center;
|
|
93
|
+
font-family: 'Press Start 2P', cursive;
|
|
94
|
+
line-height: 1.2;
|
|
95
|
+
word-break: break-word;
|
|
96
|
+
`;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { usePagination } from '../CraftBook/hooks/usePagination';
|
|
4
|
+
import { Dropdown, IOptionsProps } from '../Dropdown';
|
|
5
|
+
import { Pagination } from '../shared/Pagination/Pagination';
|
|
6
|
+
import { SearchBar } from '../shared/SearchBar/SearchBar';
|
|
7
|
+
|
|
8
|
+
interface IInformationCenterTabViewProps<T> {
|
|
9
|
+
items: T[];
|
|
10
|
+
searchQuery: string;
|
|
11
|
+
onSearchChange: (query: string) => void;
|
|
12
|
+
filterItems: (items: T[]) => T[];
|
|
13
|
+
renderContent: (items: T[]) => React.ReactNode;
|
|
14
|
+
searchPlaceholder: string;
|
|
15
|
+
filterOptions?: {
|
|
16
|
+
options: IOptionsProps[];
|
|
17
|
+
selectedOption: string;
|
|
18
|
+
onOptionChange: (value: string) => void;
|
|
19
|
+
};
|
|
20
|
+
emptyMessage?: string;
|
|
21
|
+
dependencies?: any[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ITEMS_PER_PAGE = 5;
|
|
25
|
+
|
|
26
|
+
export function InformationCenterTabView<T>({
|
|
27
|
+
items,
|
|
28
|
+
searchQuery,
|
|
29
|
+
onSearchChange,
|
|
30
|
+
filterItems,
|
|
31
|
+
renderContent,
|
|
32
|
+
searchPlaceholder,
|
|
33
|
+
filterOptions,
|
|
34
|
+
emptyMessage = 'No items found',
|
|
35
|
+
dependencies = [],
|
|
36
|
+
}: IInformationCenterTabViewProps<T>): React.ReactElement {
|
|
37
|
+
const filteredItems = filterItems(items);
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
currentPage,
|
|
41
|
+
setCurrentPage,
|
|
42
|
+
paginatedItems,
|
|
43
|
+
totalPages,
|
|
44
|
+
} = usePagination({
|
|
45
|
+
items: filteredItems,
|
|
46
|
+
itemsPerPage: ITEMS_PER_PAGE,
|
|
47
|
+
dependencies: [searchQuery, ...dependencies],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Section>
|
|
52
|
+
<HeaderContainer>
|
|
53
|
+
<StyledSearchBar
|
|
54
|
+
value={searchQuery}
|
|
55
|
+
onChange={onSearchChange}
|
|
56
|
+
placeholder={searchPlaceholder}
|
|
57
|
+
/>
|
|
58
|
+
{filterOptions && (
|
|
59
|
+
<StyledDropdown
|
|
60
|
+
options={filterOptions.options}
|
|
61
|
+
onChange={filterOptions.onOptionChange}
|
|
62
|
+
width="200px"
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
</HeaderContainer>
|
|
66
|
+
|
|
67
|
+
{paginatedItems.length === 0 ? (
|
|
68
|
+
<EmptyMessage>{emptyMessage}</EmptyMessage>
|
|
69
|
+
) : (
|
|
70
|
+
<>
|
|
71
|
+
{renderContent(paginatedItems)}
|
|
72
|
+
<StyledPagination
|
|
73
|
+
currentPage={currentPage}
|
|
74
|
+
totalPages={totalPages}
|
|
75
|
+
onPageChange={setCurrentPage}
|
|
76
|
+
/>
|
|
77
|
+
</>
|
|
78
|
+
)}
|
|
79
|
+
</Section>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const Section = styled.div`
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
gap: 1rem;
|
|
87
|
+
height: 100%;
|
|
88
|
+
min-height: 400px;
|
|
89
|
+
overflow-y: auto;
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const HeaderContainer = styled.div`
|
|
93
|
+
display: flex;
|
|
94
|
+
justify-content: space-between;
|
|
95
|
+
align-items: flex-start;
|
|
96
|
+
gap: 1rem;
|
|
97
|
+
padding: 0 1rem;
|
|
98
|
+
margin-top: 1.5rem;
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
const StyledSearchBar = styled(SearchBar)`
|
|
102
|
+
margin-bottom: 1rem;
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
const StyledDropdown = styled(Dropdown)`
|
|
106
|
+
min-width: 150px;
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
const StyledPagination = styled(Pagination)`
|
|
110
|
+
margin-top: 1rem;
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
const EmptyMessage = styled.div`
|
|
114
|
+
text-align: center;
|
|
115
|
+
color: #9ca3af;
|
|
116
|
+
padding: 2rem;
|
|
117
|
+
flex: 1;
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
justify-content: center;
|
|
121
|
+
`;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EntityAttackType,
|
|
3
|
+
ICharacterPermanentBuff,
|
|
4
|
+
IEquippableItemBlueprint,
|
|
5
|
+
INPCLoot,
|
|
6
|
+
ItemRarities,
|
|
7
|
+
NPCAlignment,
|
|
8
|
+
NPCSubtype,
|
|
9
|
+
RangeTypes,
|
|
10
|
+
} from '@rpg-engine/shared';
|
|
11
|
+
|
|
12
|
+
export enum MovementSpeed {
|
|
13
|
+
ExtraSlow = 1.5,
|
|
14
|
+
Slow = 2.25,
|
|
15
|
+
Standard = 2.6,
|
|
16
|
+
Fast = 3,
|
|
17
|
+
ExtraFast = 3.5,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export enum EntityEffectBlueprint {
|
|
21
|
+
Poison = 'poison',
|
|
22
|
+
Bleeding = 'bleeding',
|
|
23
|
+
Freezing = 'freezing',
|
|
24
|
+
Burning = 'burning',
|
|
25
|
+
Corruption = 'corruption',
|
|
26
|
+
VineGrasp = 'vine-grasp',
|
|
27
|
+
Curse = 'curse',
|
|
28
|
+
Drain = 'drain',
|
|
29
|
+
Shadow = 'shadow',
|
|
30
|
+
Stun = 'stun',
|
|
31
|
+
Knockback = 'knockback',
|
|
32
|
+
Rage = 'rage',
|
|
33
|
+
Weakness = 'weakness',
|
|
34
|
+
Cripple = 'cripple',
|
|
35
|
+
Regeneration = 'regeneration',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export enum LootProbability {
|
|
39
|
+
VeryRare = 0.5,
|
|
40
|
+
Rare = 1,
|
|
41
|
+
Uncommon = 10,
|
|
42
|
+
SemiCommon = 15,
|
|
43
|
+
Common = 20,
|
|
44
|
+
VeryCommon = 35,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface INPCBlueprintSpellArea {
|
|
48
|
+
spellKey: string;
|
|
49
|
+
probability: number;
|
|
50
|
+
power: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IInformationCenterNPC {
|
|
54
|
+
id: string;
|
|
55
|
+
name: string;
|
|
56
|
+
key: string;
|
|
57
|
+
subType: NPCSubtype;
|
|
58
|
+
alignment: NPCAlignment;
|
|
59
|
+
attackType: EntityAttackType;
|
|
60
|
+
maxRangeAttack: RangeTypes;
|
|
61
|
+
speed: MovementSpeed;
|
|
62
|
+
baseHealth: number;
|
|
63
|
+
skills: {
|
|
64
|
+
level: number;
|
|
65
|
+
strength?: { level: number };
|
|
66
|
+
dexterity?: { level: number };
|
|
67
|
+
resistance?: { level: number };
|
|
68
|
+
};
|
|
69
|
+
fleeOnLowHealth: boolean;
|
|
70
|
+
entityEffects: EntityEffectBlueprint[];
|
|
71
|
+
areaSpells: INPCBlueprintSpellArea[];
|
|
72
|
+
loots: INPCLoot[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface IInformationCenterItem extends IEquippableItemBlueprint {
|
|
76
|
+
tier: number;
|
|
77
|
+
rarity: ItemRarities;
|
|
78
|
+
rangeType?: EntityAttackType;
|
|
79
|
+
basePrice: number;
|
|
80
|
+
canSell: boolean;
|
|
81
|
+
maxStackSize: number;
|
|
82
|
+
usableEffectDescription?: string;
|
|
83
|
+
entityEffects?: [EntityEffectBlueprint];
|
|
84
|
+
entityEffectChance?: number;
|
|
85
|
+
equippedBuff?: ICharacterPermanentBuff[];
|
|
86
|
+
equippedBuffDescription?: string;
|
|
87
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Portal } from '../../../shared/Portal/Portal';
|
|
4
|
+
import { InformationCenterCell } from '../../InformationCenterCell';
|
|
5
|
+
import { InformationCenterTabView } from '../../InformationCenterTabView';
|
|
6
|
+
import { IInformationCenterNPC } from '../../InformationCenterTypes';
|
|
7
|
+
import { InformationCenterNPCDetails } from './InformationCenterNPCDetails';
|
|
8
|
+
import { InformationCenterNPCTooltip } from './InformationCenterNPCTooltip';
|
|
9
|
+
|
|
10
|
+
interface IBestiarySectionProps {
|
|
11
|
+
bestiaryItems: IInformationCenterNPC[];
|
|
12
|
+
itemsAtlasJSON: Record<string, any>;
|
|
13
|
+
itemsAtlasIMG: string;
|
|
14
|
+
entitiesAtlasJSON: Record<string, any>;
|
|
15
|
+
entitiesAtlasIMG: string;
|
|
16
|
+
initialSearchQuery: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const BestiarySection: React.FC<IBestiarySectionProps> = ({
|
|
20
|
+
bestiaryItems,
|
|
21
|
+
itemsAtlasJSON,
|
|
22
|
+
itemsAtlasIMG,
|
|
23
|
+
entitiesAtlasJSON,
|
|
24
|
+
entitiesAtlasIMG,
|
|
25
|
+
initialSearchQuery,
|
|
26
|
+
}) => {
|
|
27
|
+
const [bestiarySearchQuery, setBestiarySearchQuery] = useState(
|
|
28
|
+
initialSearchQuery
|
|
29
|
+
);
|
|
30
|
+
const [tooltipData, setTooltipData] = useState<{
|
|
31
|
+
npc: IInformationCenterNPC;
|
|
32
|
+
position: { x: number; y: number };
|
|
33
|
+
} | null>(null);
|
|
34
|
+
const [
|
|
35
|
+
selectedMonster,
|
|
36
|
+
setSelectedMonster,
|
|
37
|
+
] = useState<IInformationCenterNPC | null>(null);
|
|
38
|
+
const [isTouchDevice] = useState('ontouchstart' in window);
|
|
39
|
+
|
|
40
|
+
const filterItems = (items: IInformationCenterNPC[]) => {
|
|
41
|
+
return items.filter(item =>
|
|
42
|
+
item.name.toLowerCase().includes(bestiarySearchQuery.toLowerCase())
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const handleMouseEnter = (
|
|
47
|
+
monster: IInformationCenterNPC,
|
|
48
|
+
event: React.MouseEvent
|
|
49
|
+
) => {
|
|
50
|
+
if (!isTouchDevice && !selectedMonster) {
|
|
51
|
+
setTooltipData({
|
|
52
|
+
npc: monster,
|
|
53
|
+
position: { x: event.clientX, y: event.clientY },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const handleMouseLeave = () => {
|
|
59
|
+
if (!isTouchDevice) {
|
|
60
|
+
setTooltipData(null);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const handleMouseMove = (event: React.MouseEvent) => {
|
|
65
|
+
if (!isTouchDevice && tooltipData) {
|
|
66
|
+
setTooltipData({
|
|
67
|
+
...tooltipData,
|
|
68
|
+
position: { x: event.clientX, y: event.clientY },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const handleTouchStart = (
|
|
74
|
+
monster: IInformationCenterNPC,
|
|
75
|
+
event: React.TouchEvent
|
|
76
|
+
) => {
|
|
77
|
+
if (isTouchDevice) {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
const touch = event.touches[0];
|
|
80
|
+
if (tooltipData?.npc.id === monster.id) {
|
|
81
|
+
setTooltipData(null);
|
|
82
|
+
} else {
|
|
83
|
+
setTooltipData({
|
|
84
|
+
npc: monster,
|
|
85
|
+
position: { x: touch.clientX, y: touch.clientY },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleMonsterClick = (monster: IInformationCenterNPC) => {
|
|
92
|
+
setSelectedMonster(monster);
|
|
93
|
+
setTooltipData(null);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const renderContent = (items: IInformationCenterNPC[]) => (
|
|
97
|
+
<BestiaryGrid>
|
|
98
|
+
{items.map(item => (
|
|
99
|
+
<InformationCenterCell
|
|
100
|
+
key={item.id}
|
|
101
|
+
name={item.name}
|
|
102
|
+
spriteKey={item.key}
|
|
103
|
+
atlasJSON={entitiesAtlasJSON}
|
|
104
|
+
atlasIMG={entitiesAtlasIMG}
|
|
105
|
+
onClick={() => handleMonsterClick(item)}
|
|
106
|
+
onMouseEnter={e => handleMouseEnter(item, e)}
|
|
107
|
+
onMouseLeave={handleMouseLeave}
|
|
108
|
+
onMouseMove={handleMouseMove}
|
|
109
|
+
onTouchStart={e => handleTouchStart(item, e)}
|
|
110
|
+
/>
|
|
111
|
+
))}
|
|
112
|
+
</BestiaryGrid>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<>
|
|
117
|
+
<InformationCenterTabView
|
|
118
|
+
items={bestiaryItems}
|
|
119
|
+
searchQuery={bestiarySearchQuery}
|
|
120
|
+
onSearchChange={setBestiarySearchQuery}
|
|
121
|
+
filterItems={filterItems}
|
|
122
|
+
renderContent={renderContent}
|
|
123
|
+
searchPlaceholder="Search monsters..."
|
|
124
|
+
emptyMessage="No monsters found"
|
|
125
|
+
/>
|
|
126
|
+
{tooltipData && (
|
|
127
|
+
<Portal>
|
|
128
|
+
<TooltipWrapper
|
|
129
|
+
style={{
|
|
130
|
+
position: 'fixed',
|
|
131
|
+
left: tooltipData.position.x + 10,
|
|
132
|
+
top: tooltipData.position.y + 10,
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<InformationCenterNPCTooltip
|
|
136
|
+
npc={tooltipData.npc}
|
|
137
|
+
itemsAtlasJSON={itemsAtlasJSON}
|
|
138
|
+
itemsAtlasIMG={itemsAtlasIMG}
|
|
139
|
+
/>
|
|
140
|
+
</TooltipWrapper>
|
|
141
|
+
</Portal>
|
|
142
|
+
)}
|
|
143
|
+
{selectedMonster && (
|
|
144
|
+
<InformationCenterNPCDetails
|
|
145
|
+
npc={selectedMonster}
|
|
146
|
+
itemsAtlasJSON={itemsAtlasJSON}
|
|
147
|
+
itemsAtlasIMG={itemsAtlasIMG}
|
|
148
|
+
entitiesAtlasJSON={entitiesAtlasJSON}
|
|
149
|
+
entitiesAtlasIMG={entitiesAtlasIMG}
|
|
150
|
+
onBack={() => setSelectedMonster(null)}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
153
|
+
</>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const BestiaryGrid = styled.div`
|
|
158
|
+
display: grid;
|
|
159
|
+
grid-template-columns: repeat(4, 1fr);
|
|
160
|
+
gap: 1rem;
|
|
161
|
+
padding: 0 1rem;
|
|
162
|
+
min-height: 300px;
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
const TooltipWrapper = styled.div`
|
|
166
|
+
position: fixed;
|
|
167
|
+
z-index: 1000;
|
|
168
|
+
pointer-events: none;
|
|
169
|
+
width: 300px;
|
|
170
|
+
`;
|