@rpg-engine/long-bow 0.8.27 → 0.8.29

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": "@rpg-engine/long-bow",
3
- "version": "0.8.27",
3
+ "version": "0.8.29",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -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
  );
@@ -1,4 +1,8 @@
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';
4
8
  import { IOptionsProps } from '../../../Dropdown';
@@ -8,6 +12,7 @@ import { InformationCenterCell } from '../../InformationCenterCell';
8
12
  import { formatItemType } from '../items/InformationCenterItemsSection';
9
13
  import { InformationCenterNPCDetails } from './InformationCenterNPCDetails';
10
14
  import { InformationCenterNPCTooltip } from './InformationCenterNPCTooltip';
15
+
11
16
  interface IBestiarySectionProps {
12
17
  bestiaryItems: IInformationCenterNPC[];
13
18
  itemsAtlasJSON: Record<string, any>;
@@ -31,6 +36,7 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
31
36
  initialSearchQuery,
32
37
  tabId,
33
38
  }) => {
39
+ const isMobile = isMobileOrTablet();
34
40
  const [searchQuery, setSearchQuery] = useState(initialSearchQuery);
35
41
  const [tooltipData, setTooltipData] = useState<{
36
42
  npc: IInformationCenterNPC;
@@ -40,13 +46,12 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
40
46
  selectedMonster,
41
47
  setSelectedMonster,
42
48
  ] = useState<IInformationCenterNPC | null>(null);
43
- const [isTouchDevice] = useState('ontouchstart' in window);
44
49
 
45
50
  const handleMouseEnter = (
46
51
  monster: IInformationCenterNPC,
47
52
  event: React.MouseEvent
48
53
  ) => {
49
- if (!isTouchDevice && !selectedMonster) {
54
+ if (!isMobile && !selectedMonster) {
50
55
  setTooltipData({
51
56
  npc: monster,
52
57
  position: { x: event.clientX, y: event.clientY },
@@ -55,13 +60,13 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
55
60
  };
56
61
 
57
62
  const handleMouseLeave = () => {
58
- if (!isTouchDevice) {
63
+ if (!isMobile) {
59
64
  setTooltipData(null);
60
65
  }
61
66
  };
62
67
 
63
68
  const handleMouseMove = (event: React.MouseEvent) => {
64
- if (!isTouchDevice && tooltipData) {
69
+ if (!isMobile && tooltipData) {
65
70
  setTooltipData({
66
71
  ...tooltipData,
67
72
  position: { x: event.clientX, y: event.clientY },
@@ -73,18 +78,9 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
73
78
  monster: IInformationCenterNPC,
74
79
  event: React.TouchEvent
75
80
  ) => {
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
- }
81
+ event.preventDefault();
82
+ setSelectedMonster(monster);
83
+ setTooltipData(null);
88
84
  };
89
85
 
90
86
  const handleMonsterClick = (monster: IInformationCenterNPC) => {
@@ -170,7 +166,7 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
170
166
  dependencies={[selectedBestiaryCategory]}
171
167
  itemHeight="180px"
172
168
  />
173
- {tooltipData && (
169
+ {!isMobile && tooltipData && (
174
170
  <Portal>
175
171
  <TooltipWrapper
176
172
  style={{
@@ -188,16 +184,18 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
188
184
  </Portal>
189
185
  )}
190
186
  {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
- />
187
+ <Portal>
188
+ <InformationCenterNPCDetails
189
+ npc={selectedMonster}
190
+ itemsAtlasJSON={itemsAtlasJSON}
191
+ itemsAtlasIMG={itemsAtlasIMG}
192
+ iconAtlasIMG={iconsAtlasIMG}
193
+ iconAtlasJSON={iconsAtlasJSON}
194
+ entitiesAtlasJSON={entitiesAtlasJSON}
195
+ entitiesAtlasIMG={entitiesAtlasIMG}
196
+ onBack={() => setSelectedMonster(null)}
197
+ />
198
+ </Portal>
201
199
  )}
202
200
  </>
203
201
  );
@@ -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`
@@ -2,11 +2,13 @@ import {
2
2
  IInformationCenterItem,
3
3
  IInformationCenterNPC,
4
4
  ItemType,
5
+ isMobileOrTablet,
5
6
  } from '@rpg-engine/shared';
6
7
  import React, { useState } from 'react';
7
8
  import styled from 'styled-components';
8
9
  import { IOptionsProps } from '../../../Dropdown';
9
10
  import { PaginatedContent } from '../../../shared/PaginatedContent/PaginatedContent';
11
+ import { Portal } from '../../../shared/Portal/Portal';
10
12
  import { InformationCenterCell } from '../../InformationCenterCell';
11
13
  import { InformationCenterItemDetails } from './InformationCenterItemDetails';
12
14
  import { InformationCenterItemTooltip } from './InformationCenterItemTooltip';
@@ -47,6 +49,7 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
47
49
  initialSearchQuery,
48
50
  tabId,
49
51
  }) => {
52
+ const isMobile = isMobileOrTablet();
50
53
  const [searchQuery, setSearchQuery] = useState(initialSearchQuery);
51
54
  const [selectedItemCategory, setSelectedItemCategory] = useState<string>(
52
55
  'all'
@@ -90,15 +93,17 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
90
93
  e: React.MouseEvent,
91
94
  item: IInformationCenterItem
92
95
  ) => {
93
- setTooltipPosition({
94
- x: e.clientX + TOOLTIP_OFFSET,
95
- y: e.clientY,
96
- });
97
- setHoveredItem(item);
96
+ if (!isMobile) {
97
+ setTooltipPosition({
98
+ x: e.clientX + TOOLTIP_OFFSET,
99
+ y: e.clientY,
100
+ });
101
+ setHoveredItem(item);
102
+ }
98
103
  };
99
104
 
100
105
  const handleMouseMove = (e: React.MouseEvent) => {
101
- if (hoveredItem) {
106
+ if (!isMobile && hoveredItem) {
102
107
  setTooltipPosition({
103
108
  x: e.clientX + TOOLTIP_OFFSET,
104
109
  y: e.clientY,
@@ -107,19 +112,17 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
107
112
  };
108
113
 
109
114
  const handleMouseLeave = () => {
110
- setHoveredItem(null);
115
+ if (!isMobile) {
116
+ setHoveredItem(null);
117
+ }
111
118
  };
112
119
 
113
120
  const handleTouchStart = (
114
121
  e: React.TouchEvent,
115
122
  item: IInformationCenterItem
116
123
  ) => {
117
- const touch = e.touches[0];
118
- setTooltipPosition({
119
- x: touch.clientX + TOOLTIP_OFFSET,
120
- y: touch.clientY,
121
- });
122
- setHoveredItem(item);
124
+ e.preventDefault();
125
+ setSelectedItem(item);
123
126
  };
124
127
 
125
128
  const handleItemClick = (item: IInformationCenterItem) => {
@@ -170,7 +173,7 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
170
173
  layout="grid"
171
174
  itemHeight="180px"
172
175
  />
173
- {hoveredItem && (
176
+ {!isMobile && hoveredItem && (
174
177
  <TooltipWrapper
175
178
  style={{ top: tooltipPosition.y, left: tooltipPosition.x }}
176
179
  >
@@ -178,13 +181,15 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
178
181
  </TooltipWrapper>
179
182
  )}
180
183
  {selectedItem && (
181
- <InformationCenterItemDetails
182
- item={selectedItem}
183
- itemsAtlasJSON={itemsAtlasJSON}
184
- itemsAtlasIMG={itemsAtlasIMG}
185
- droppedBy={getDroppedByNPCs(selectedItem.key, bestiaryItems)}
186
- onBack={() => setSelectedItem(null)}
187
- />
184
+ <Portal>
185
+ <InformationCenterItemDetails
186
+ item={selectedItem}
187
+ itemsAtlasJSON={itemsAtlasJSON}
188
+ itemsAtlasIMG={itemsAtlasIMG}
189
+ droppedBy={getDroppedByNPCs(selectedItem.key, bestiaryItems)}
190
+ onBack={() => setSelectedItem(null)}
191
+ />
192
+ </Portal>
188
193
  )}
189
194
  </>
190
195
  );
@@ -49,32 +49,45 @@ export const BaseInformationDetails: React.FC<IBaseInformationDetailsProps> = ({
49
49
  };
50
50
 
51
51
  const Container = styled.div`
52
- position: absolute;
52
+ position: fixed;
53
53
  inset: 0;
54
54
  display: flex;
55
55
  justify-content: center;
56
56
  align-items: center;
57
- z-index: 1000;
57
+ z-index: 9999;
58
58
  `;
59
59
 
60
60
  const Overlay = styled.div`
61
- position: absolute;
61
+ position: fixed;
62
62
  inset: 0;
63
63
  background-color: rgba(0, 0, 0, 0.8);
64
64
  `;
65
65
 
66
66
  const Modal = styled.div`
67
- position: relative;
68
- width: 90%;
69
- height: 90%;
67
+ position: fixed;
70
68
  background-color: rgba(0, 0, 0, 0.95);
71
69
  border-radius: 4px;
72
- padding: 16px;
73
- overflow-y: auto;
70
+ padding: 12px;
71
+ overflow: hidden;
74
72
  z-index: 1;
75
73
  font-family: 'Press Start 2P', cursive;
76
74
  border: 1px solid ${uiColors.darkGray};
77
75
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
76
+ width: 90%;
77
+ height: 90%;
78
+ top: 5%;
79
+ left: 5%;
80
+ max-width: 800px;
81
+ margin: 0 auto;
82
+
83
+ @media (max-width: 768px) {
84
+ width: 100%;
85
+ height: 100%;
86
+ top: 0;
87
+ left: 0;
88
+ border-radius: 0;
89
+ padding: 8px;
90
+ }
78
91
 
79
92
  &::-webkit-scrollbar {
80
93
  width: 2px;
@@ -126,9 +139,19 @@ const Header = styled.div`
126
139
  const Content = styled.div`
127
140
  display: flex;
128
141
  flex-direction: column;
129
- gap: 16px;
130
- height: 100%;
131
- overflow: auto;
142
+ gap: 12px;
143
+ height: calc(100% - 80px);
144
+ overflow-y: auto;
145
+ overflow-x: hidden;
146
+ padding-right: 6px;
147
+ margin-right: -6px;
148
+
149
+ @media (max-width: 768px) {
150
+ height: calc(100% - 64px);
151
+ gap: 8px;
152
+ padding-right: 4px;
153
+ margin-right: -4px;
154
+ }
132
155
  `;
133
156
 
134
157
  const Title = styled.h2`
@@ -66,5 +66,5 @@ const Icon = styled.span`
66
66
  `;
67
67
 
68
68
  const Content = styled.div`
69
- padding: 12px;
69
+ padding: 6px;
70
70
  `;