@rpg-engine/long-bow 0.8.227 → 0.8.229

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.227",
3
+ "version": "0.8.229",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -0,0 +1,151 @@
1
+ import { ICharacter, ICharacterListing } from '@rpg-engine/shared';
2
+ import React, { useState } from 'react';
3
+ import styled from 'styled-components';
4
+ import { CharacterMarketplacePanel } from './CharacterMarketplacePanel';
5
+ import { MyCharacterListingsPanel } from './MyCharacterListingsPanel';
6
+
7
+ export interface ICharacterTradePanelProps {
8
+ characterListings: ICharacterListing[];
9
+ characterListingsTotal: number;
10
+ characterListingsPage: number;
11
+ characterListingsItemsPerPage: number;
12
+ onCharacterListingsPageChange: (page: number) => void;
13
+ onCharacterBuy: (listingId: string) => void;
14
+ characterNameFilter?: string;
15
+ onCharacterNameFilterChange?: (name: string) => void;
16
+ characterListingsLoading?: boolean;
17
+ myCharacterListings: ICharacterListing[];
18
+ myCharacterListingsTotal: number;
19
+ myCharacterListingsPage: number;
20
+ onMyCharacterListingsPageChange: (page: number) => void;
21
+ onCharacterDelist: (listingId: string) => void;
22
+ accountCharacters: ICharacter[];
23
+ onCharacterList: (characterId: string, price: number) => void;
24
+ activeCharacterId?: string;
25
+ atlasJSON: any;
26
+ atlasIMG: any;
27
+ characterAtlasJSON?: any;
28
+ characterAtlasIMG?: any;
29
+ enableHotkeys?: () => void;
30
+ disableHotkeys?: () => void;
31
+ }
32
+
33
+ export const CharacterTradePanel: React.FC<ICharacterTradePanelProps> = ({
34
+ characterListings,
35
+ characterListingsTotal,
36
+ characterListingsPage,
37
+ characterListingsItemsPerPage,
38
+ onCharacterListingsPageChange,
39
+ onCharacterBuy,
40
+ characterNameFilter,
41
+ onCharacterNameFilterChange,
42
+ characterListingsLoading,
43
+ myCharacterListings,
44
+ myCharacterListingsTotal,
45
+ myCharacterListingsPage,
46
+ onMyCharacterListingsPageChange,
47
+ onCharacterDelist,
48
+ accountCharacters,
49
+ onCharacterList,
50
+ activeCharacterId,
51
+ atlasJSON,
52
+ atlasIMG,
53
+ characterAtlasJSON,
54
+ characterAtlasIMG,
55
+ enableHotkeys,
56
+ disableHotkeys,
57
+ }) => {
58
+ const [subTab, setSubTab] = useState<'browse' | 'my-listings'>('browse');
59
+
60
+ return (
61
+ <>
62
+ <CharacterSubTabs>
63
+ <CharacterSubTab
64
+ $active={subTab === 'browse'}
65
+ onClick={() => setSubTab('browse')}
66
+ type="button"
67
+ >
68
+ Browse
69
+ </CharacterSubTab>
70
+ <CharacterSubTab
71
+ $active={subTab === 'my-listings'}
72
+ onClick={() => setSubTab('my-listings')}
73
+ type="button"
74
+ >
75
+ My Listings
76
+ </CharacterSubTab>
77
+ </CharacterSubTabs>
78
+
79
+ {subTab === 'browse' && (
80
+ <CharacterMarketplacePanel
81
+ characterListings={characterListings}
82
+ totalCount={characterListingsTotal}
83
+ currentPage={characterListingsPage}
84
+ itemsPerPage={characterListingsItemsPerPage}
85
+ onPageChange={onCharacterListingsPageChange}
86
+ onCharacterBuy={onCharacterBuy}
87
+ atlasJSON={atlasJSON}
88
+ atlasIMG={atlasIMG}
89
+ characterAtlasJSON={characterAtlasJSON ?? atlasJSON}
90
+ characterAtlasIMG={characterAtlasIMG ?? atlasIMG}
91
+ enableHotkeys={enableHotkeys}
92
+ disableHotkeys={disableHotkeys}
93
+ nameFilter={characterNameFilter}
94
+ onNameFilterChange={onCharacterNameFilterChange}
95
+ isLoading={characterListingsLoading}
96
+ />
97
+ )}
98
+
99
+ {subTab === 'my-listings' && (
100
+ <MyCharacterListingsPanel
101
+ myCharacterListings={myCharacterListings}
102
+ totalCount={myCharacterListingsTotal}
103
+ currentPage={myCharacterListingsPage}
104
+ itemsPerPage={10}
105
+ onPageChange={onMyCharacterListingsPageChange}
106
+ onCharacterDelist={onCharacterDelist}
107
+ accountCharacters={accountCharacters}
108
+ onCharacterList={onCharacterList}
109
+ activeCharacterId={activeCharacterId}
110
+ atlasJSON={atlasJSON}
111
+ atlasIMG={atlasIMG}
112
+ characterAtlasJSON={characterAtlasJSON ?? atlasJSON}
113
+ characterAtlasIMG={characterAtlasIMG ?? atlasIMG}
114
+ enableHotkeys={enableHotkeys}
115
+ disableHotkeys={disableHotkeys}
116
+ />
117
+ )}
118
+ </>
119
+ );
120
+ };
121
+
122
+ const CharacterSubTabs = styled.div`
123
+ display: flex;
124
+ gap: 8px;
125
+ width: 95%;
126
+ margin: 10px auto 0 auto;
127
+ padding: 8px;
128
+ background: rgba(0, 0, 0, 0.2);
129
+ border-radius: 4px;
130
+ border: 1px solid rgba(255, 255, 255, 0.05);
131
+ `;
132
+
133
+ const CharacterSubTab = styled.button<{ $active: boolean }>`
134
+ flex: 1;
135
+ padding: 8px 12px;
136
+ font-family: 'Press Start 2P', cursive;
137
+ font-size: 0.45rem;
138
+ border-radius: 4px;
139
+ border: 1px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.12)')};
140
+ background: ${({ $active }) => ($active ? 'rgba(245,158,11,0.15)' : 'rgba(0,0,0,0.3)')};
141
+ color: ${({ $active }) => ($active ? '#f59e0b' : '#777')};
142
+ cursor: pointer;
143
+ transition: border-color 0.15s, background 0.15s, color 0.15s;
144
+ text-transform: uppercase;
145
+ letter-spacing: 0.5px;
146
+
147
+ &:hover {
148
+ border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.3)')};
149
+ color: ${({ $active }) => ($active ? '#f59e0b' : '#bbb')};
150
+ }
151
+ `;
@@ -30,8 +30,7 @@ import { HistoryPanel } from './HistoryPanel';
30
30
  import { ManagmentPanel } from './ManagmentPanel';
31
31
  import { MarketplacePaymentMethod } from './MarketplaceBuyModal';
32
32
  import { MarketplaceAcceptedCurrency, MarketplaceSettingsPanel } from './MarketplaceSettingsPanel';
33
- import { CharacterMarketplacePanel } from './CharacterMarketplacePanel';
34
- import { MyCharacterListingsPanel } from './MyCharacterListingsPanel';
33
+ import { CharacterTradePanel } from './CharacterTradePanel';
35
34
 
36
35
  export interface IMarketPlaceProps {
37
36
  items: IMarketplaceItem[];
@@ -207,8 +206,6 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
207
206
  const [activeTab, setActiveTab] = useState<ActiveTab>('marketplace');
208
207
  const [acceptedCurrency, setAcceptedCurrency] = useState<MarketplaceAcceptedCurrency>(acceptedCurrencyProp ?? MarketplaceAcceptedCurrency.GoldOrDc);
209
208
  const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
210
- const [characterSubTab, setCharacterSubTab] = useState<'browse' | 'my-listings'>('browse');
211
-
212
209
  const handleCurrencyChange = (value: MarketplaceAcceptedCurrency) => {
213
210
  setAcceptedCurrency(value);
214
211
  onAcceptedCurrencyChange?.(value);
@@ -292,65 +289,31 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
292
289
  )}
293
290
 
294
291
  {activeTab === 'characters' && (
295
- <>
296
- <CharacterSubTabs>
297
- <CharacterSubTab
298
- $active={characterSubTab === 'browse'}
299
- onClick={() => setCharacterSubTab('browse')}
300
- type="button"
301
- >
302
- Browse
303
- </CharacterSubTab>
304
- <CharacterSubTab
305
- $active={characterSubTab === 'my-listings'}
306
- onClick={() => setCharacterSubTab('my-listings')}
307
- type="button"
308
- >
309
- My Listings
310
- </CharacterSubTab>
311
- </CharacterSubTabs>
312
-
313
- {characterSubTab === 'browse' && (
314
- <CharacterMarketplacePanel
315
- characterListings={characterListings ?? []}
316
- totalCount={characterListingsTotal ?? 0}
317
- currentPage={characterListingsPage ?? 1}
318
- itemsPerPage={characterListingsItemsPerPage ?? 10}
319
- onPageChange={onCharacterListingsPageChange ?? (() => {})}
320
- onCharacterBuy={onCharacterBuy ?? (() => {})}
321
- atlasJSON={props.atlasJSON}
322
- atlasIMG={props.atlasIMG}
323
- characterAtlasJSON={characterAtlasJSON ?? props.atlasJSON}
324
- characterAtlasIMG={characterAtlasIMG ?? props.atlasIMG}
325
- enableHotkeys={props.enableHotkeys}
326
- disableHotkeys={props.disableHotkeys}
327
- nameFilter={characterNameFilter}
328
- onNameFilterChange={onCharacterNameFilterChange}
329
- isLoading={characterListingsLoading}
330
- />
331
- )}
332
-
333
- {characterSubTab === 'my-listings' && (
334
- <MyCharacterListingsPanel
335
- myCharacterListings={myCharacterListings ?? []}
336
- totalCount={myCharacterListingsTotal ?? 0}
337
- currentPage={myCharacterListingsPage ?? 1}
338
- itemsPerPage={10}
339
- onPageChange={onMyCharacterListingsPageChange ?? (() => {})}
340
- onCharacterDelist={onCharacterDelist ?? (() => {})}
341
- accountCharacters={accountCharacters ?? []}
342
- onCharacterList={onCharacterList ?? (() => {})}
343
- activeCharacterId={props.characterId}
344
- atlasJSON={props.atlasJSON}
345
- atlasIMG={props.atlasIMG}
346
- characterAtlasJSON={characterAtlasJSON ?? props.atlasJSON}
347
- characterAtlasIMG={characterAtlasIMG ?? props.atlasIMG}
348
- enableHotkeys={props.enableHotkeys}
349
- disableHotkeys={props.disableHotkeys}
350
- />
351
- )}
352
-
353
- </>
292
+ <CharacterTradePanel
293
+ characterListings={characterListings ?? []}
294
+ characterListingsTotal={characterListingsTotal ?? 0}
295
+ characterListingsPage={characterListingsPage ?? 1}
296
+ characterListingsItemsPerPage={characterListingsItemsPerPage ?? 10}
297
+ onCharacterListingsPageChange={onCharacterListingsPageChange ?? (() => {})}
298
+ onCharacterBuy={onCharacterBuy ?? (() => {})}
299
+ characterNameFilter={characterNameFilter}
300
+ onCharacterNameFilterChange={onCharacterNameFilterChange}
301
+ characterListingsLoading={characterListingsLoading}
302
+ myCharacterListings={myCharacterListings ?? []}
303
+ myCharacterListingsTotal={myCharacterListingsTotal ?? 0}
304
+ myCharacterListingsPage={myCharacterListingsPage ?? 1}
305
+ onMyCharacterListingsPageChange={onMyCharacterListingsPageChange ?? (() => {})}
306
+ onCharacterDelist={onCharacterDelist ?? (() => {})}
307
+ accountCharacters={accountCharacters ?? []}
308
+ onCharacterList={onCharacterList ?? (() => {})}
309
+ activeCharacterId={props.characterId}
310
+ atlasJSON={props.atlasJSON}
311
+ atlasIMG={props.atlasIMG}
312
+ characterAtlasJSON={characterAtlasJSON}
313
+ characterAtlasIMG={characterAtlasIMG}
314
+ enableHotkeys={props.enableHotkeys}
315
+ disableHotkeys={props.disableHotkeys}
316
+ />
354
317
  )}
355
318
 
356
319
  {activeTab === 'sell' && (
@@ -442,33 +405,3 @@ const PagerContainer = styled.div`
442
405
  padding: 4px 10px;
443
406
  `;
444
407
 
445
- const CharacterSubTabs = styled.div`
446
- display: flex;
447
- gap: 8px;
448
- width: 95%;
449
- margin: 10px auto 0 auto;
450
- padding: 8px;
451
- background: rgba(0, 0, 0, 0.2);
452
- border-radius: 4px;
453
- border: 1px solid rgba(255, 255, 255, 0.05);
454
- `;
455
-
456
- const CharacterSubTab = styled.button<{ $active: boolean }>`
457
- flex: 1;
458
- padding: 8px 12px;
459
- font-family: 'Press Start 2P', cursive;
460
- font-size: 0.45rem;
461
- border-radius: 4px;
462
- border: 1px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.12)')};
463
- background: ${({ $active }) => ($active ? 'rgba(245,158,11,0.15)' : 'rgba(0,0,0,0.3)')};
464
- color: ${({ $active }) => ($active ? '#f59e0b' : '#777')};
465
- cursor: pointer;
466
- transition: border-color 0.15s, background 0.15s, color 0.15s;
467
- text-transform: uppercase;
468
- letter-spacing: 0.5px;
469
-
470
- &:hover {
471
- border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.3)')};
472
- color: ${({ $active }) => ($active ? '#f59e0b' : '#bbb')};
473
- }
474
- `;
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { FaTimes } from 'react-icons/fa';
2
3
  import styled from 'styled-components';
3
4
  import { RPGUIContainer, RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
4
5
  import { NPCDialogText } from './NPCDialogText';
@@ -65,9 +66,8 @@ export const NPCDialog: React.FC<INPCDialogProps> = ({
65
66
  ) : (
66
67
  <>
67
68
  <Container>
68
- <CloseIcon onPointerDown={onClose}>X</CloseIcon>
69
69
  <TextContainer
70
- flex={type === NPCDialogType.TextAndThumbnail ? '70%' : '100%'}
70
+ flex={type === NPCDialogType.TextAndThumbnail ? '1' : '100%'}
71
71
  >
72
72
  <NPCDialogText
73
73
  type={type}
@@ -82,6 +82,9 @@ export const NPCDialog: React.FC<INPCDialogProps> = ({
82
82
  </TextContainer>
83
83
  {type === NPCDialogType.TextAndThumbnail && (
84
84
  <ThumbnailContainer>
85
+ <CloseButton onPointerDown={onClose}>
86
+ <FaTimes size={12} />
87
+ </CloseButton>
85
88
  <NPCThumbnail src={imagePath || aliceDefaultThumbnail} />
86
89
  </ThumbnailContainer>
87
90
  )}
@@ -96,20 +99,41 @@ const Container = styled.div`
96
99
  display: flex;
97
100
  width: 100%;
98
101
  height: 100%;
99
-
100
102
  box-sizing: border-box;
101
- justify-content: center;
102
- align-items: flex-start;
103
+ justify-content: flex-start;
104
+ align-items: center;
103
105
  position: relative;
106
+ padding: 1rem 1rem 0.5rem 1rem;
104
107
  `;
105
108
 
106
- const CloseIcon = styled.p`
109
+ const CloseButton = styled.button`
107
110
  position: absolute;
108
- top: -1.5rem;
109
- right: -0.4rem;
111
+ top: -12px;
112
+ right: -12px;
113
+ width: 26px;
114
+ height: 26px;
115
+ background: #000;
116
+ border: 2px solid #fff;
117
+ border-radius: 2px;
118
+ color: #fff;
119
+ display: flex;
120
+ justify-content: center;
121
+ align-items: center;
110
122
  cursor: pointer;
111
- color: white;
112
- font-size: 1.1rem !important;
123
+ z-index: 10;
124
+ padding: 0;
125
+ box-shadow: 0 2px 0px rgba(0, 0, 0, 1);
126
+ transition: all 0.1s;
127
+
128
+ &:hover {
129
+ background: #333;
130
+ border-color: #eee;
131
+ }
132
+
133
+ &:active {
134
+ transform: translateY(2px);
135
+ box-shadow: 0 0 0px rgba(0, 0, 0, 1);
136
+ }
113
137
  `;
114
138
 
115
139
  interface ITextContainerProps {
@@ -117,18 +141,34 @@ interface ITextContainerProps {
117
141
  }
118
142
 
119
143
  const TextContainer = styled.div<ITextContainerProps>`
120
- flex: ${({ flex }) => flex} 0 0;
121
- width: 355px;
144
+ flex: ${({ flex }) => flex};
145
+ height: 100%;
146
+ padding-right: 1.5rem;
147
+ display: flex;
148
+ flex-direction: column;
122
149
  `;
123
150
 
124
151
  const ThumbnailContainer = styled.div`
125
- flex: 30% 0 0;
152
+ flex-shrink: 0;
153
+ position: relative;
154
+ width: 116px;
155
+ height: 116px;
156
+ /* Layered pixel-art borders: inner black -> gold -> outer black */
157
+ border: 4px solid #1a100c;
158
+ border-radius: 2px;
159
+ box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.4), 0 0 0 2px #d4a373, 0 0 0 4px #000;
160
+ background: #3e2723;
126
161
  display: flex;
127
- justify-content: flex-end;
162
+ justify-content: center;
163
+ align-items: center;
164
+ margin-right: 8px;
165
+ padding: 2px;
128
166
  `;
129
167
 
130
168
  const NPCThumbnail = styled.img`
131
169
  image-rendering: pixelated;
132
- height: 128px;
133
- width: 128px;
170
+ height: 100%;
171
+ width: 100%;
172
+ object-fit: contain;
173
+ border-radius: 1px;
134
174
  `;
@@ -118,14 +118,24 @@ export const NPCDialogText: React.FC<IProps> = ({
118
118
  );
119
119
  };
120
120
 
121
- const Container = styled.div``;
121
+ const Container = styled.div`
122
+ width: 100%;
123
+ height: 100%;
124
+ display: flex;
125
+ flex-direction: column;
126
+ position: relative;
127
+ padding-bottom: 24px;
128
+ `;
122
129
 
123
130
  const TextContainer = styled.p`
124
- font-size: 0.7rem !important;
131
+ font-size: 0.8rem !important;
132
+ line-height: 1.6;
125
133
  color: white;
126
134
  text-shadow: 1px 1px 0px #000000;
127
135
  letter-spacing: 1.2px;
128
136
  word-break: normal;
137
+ margin: 0;
138
+ flex: 1;
129
139
  `;
130
140
 
131
141
  interface IPressSpaceIndicatorProps {
@@ -134,8 +144,11 @@ interface IPressSpaceIndicatorProps {
134
144
 
135
145
  const PressSpaceIndicator = styled.img<IPressSpaceIndicatorProps>`
136
146
  position: absolute;
137
- right: ${({ right }) => right};
138
- bottom: 1rem;
139
- height: 20.7px;
147
+ left: 0;
148
+ bottom: -0.5rem;
149
+ height: 20px;
150
+ object-fit: contain;
151
+ object-position: left;
140
152
  image-rendering: -webkit-optimize-contrast;
153
+ cursor: pointer;
141
154
  `;
@@ -41,9 +41,11 @@ export const DynamicText: React.FC<IProps> = ({ text, onFinish, onStart }) => {
41
41
  };
42
42
 
43
43
  const TextContainer = styled.p`
44
- font-size: 0.7rem !important;
44
+ font-size: 0.8rem !important;
45
+ line-height: 1.6;
45
46
  color: white;
46
47
  text-shadow: 1px 1px 0px #000000;
47
48
  letter-spacing: 1.2px;
48
49
  word-break: normal;
50
+ margin: 0;
49
51
  `;
package/src/index.tsx CHANGED
@@ -46,6 +46,7 @@ export * from './components/Marketplace/HistoryPanel';
46
46
  export * from './components/Marketplace/BlueprintSearchModal';
47
47
  export * from './components/Marketplace/CharacterMarketplacePanel';
48
48
  export * from './components/Marketplace/CharacterMarketplaceRows';
49
+ export * from './components/Marketplace/CharacterTradePanel';
49
50
  export * from './components/Marketplace/CharacterDetailModal';
50
51
  export * from './components/Marketplace/CharacterListingForm';
51
52
  export * from './components/Marketplace/CharacterListingModal';