@rpg-engine/long-bow 0.8.160 → 0.8.162

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.160",
3
+ "version": "0.8.162",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -6,6 +6,7 @@ import {
6
6
  import { debounce } from 'lodash';
7
7
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
8
8
  import { FaTimes } from 'react-icons/fa';
9
+ import { SortVertical } from 'pixelarticons/react/SortVertical';
9
10
  import styled, { keyframes } from 'styled-components';
10
11
  import { Dropdown } from '../Dropdown';
11
12
  import { IOptionsProps } from '../Dropdown';
@@ -67,6 +68,7 @@ export const BlueprintSearchModal: React.FC<IBlueprintSearchModalProps> = ({
67
68
  const [searchName, setSearchName] = useState('');
68
69
  const [selectedType, setSelectedType] = useState('');
69
70
  const [selectedSubType, setSelectedSubType] = useState('');
71
+ const [showFilters, setShowFilters] = useState(false);
70
72
 
71
73
  const searchNameRef = useRef(searchName);
72
74
  const selectedTypeRef = useRef(selectedType);
@@ -154,22 +156,32 @@ export const BlueprintSearchModal: React.FC<IBlueprintSearchModalProps> = ({
154
156
  onFocus={disableHotkeys}
155
157
  onBlur={enableHotkeys}
156
158
  />
159
+ <FilterButton
160
+ type="button"
161
+ $active={showFilters}
162
+ onPointerDown={() => setShowFilters(v => !v)}
163
+ aria-label="Toggle filters"
164
+ >
165
+ <SortVertical width={16} height={16} />
166
+ </FilterButton>
157
167
  </InputWrapper>
158
168
 
159
- <FiltersRow>
160
- <StyledDropdown
161
- key={`type-${selectedType}`}
162
- options={typeOptions}
163
- onChange={handleTypeChange}
164
- width="100%"
165
- />
166
- <StyledDropdown
167
- key={`subtype-${selectedSubType}`}
168
- options={subTypeOptions}
169
- onChange={handleSubTypeChange}
170
- width="100%"
171
- />
172
- </FiltersRow>
169
+ {showFilters && (
170
+ <FiltersRow>
171
+ <StyledDropdown
172
+ key={`type-${selectedType}`}
173
+ options={typeOptions}
174
+ onChange={handleTypeChange}
175
+ width="100%"
176
+ />
177
+ <StyledDropdown
178
+ key={`subtype-${selectedSubType}`}
179
+ options={subTypeOptions}
180
+ onChange={handleSubTypeChange}
181
+ width="100%"
182
+ />
183
+ </FiltersRow>
184
+ )}
173
185
 
174
186
  <ResultsWrapper>
175
187
  {blueprints.length === 0 && !isLoading ? (
@@ -191,14 +203,13 @@ export const BlueprintSearchModal: React.FC<IBlueprintSearchModalProps> = ({
191
203
  )}
192
204
  </ResultsWrapper>
193
205
 
194
- <PagerContainer>
195
- <Pager
196
- totalItems={totalCount}
197
- currentPage={currentPage}
198
- itemsPerPage={BLUEPRINTS_PER_PAGE}
199
- onPageChange={handlePageChange}
200
- />
201
- </PagerContainer>
206
+ <Pager
207
+ compact
208
+ totalItems={totalCount}
209
+ currentPage={currentPage}
210
+ itemsPerPage={BLUEPRINTS_PER_PAGE}
211
+ onPageChange={handlePageChange}
212
+ />
202
213
  </ModalContent>
203
214
  </ModalContainer>
204
215
  </ModalPortal>
@@ -229,11 +240,21 @@ const ModalContent = styled.div`
229
240
  padding: 20px 24px;
230
241
  width: 600px;
231
242
  max-width: 90%;
243
+ max-height: 90dvh;
232
244
  display: flex;
233
245
  flex-direction: column;
234
246
  gap: 12px;
247
+ overflow: hidden;
235
248
  pointer-events: auto;
236
249
  animation: ${scaleIn} 0.15s ease-out;
250
+
251
+ @media (max-width: 950px) {
252
+ max-width: 96%;
253
+ max-height: 95dvh;
254
+ min-height: 75dvh;
255
+ padding: 14px 16px;
256
+ gap: 8px;
257
+ }
237
258
  `;
238
259
 
239
260
  const Header = styled.div`
@@ -288,6 +309,26 @@ const StyledInput = styled(Input)`
288
309
  flex: 1;
289
310
  `;
290
311
 
312
+ const FilterButton = styled.button<{ $active: boolean }>`
313
+ flex-shrink: 0;
314
+ width: 28px;
315
+ height: 28px;
316
+ border-radius: 6px;
317
+ border: 1px solid ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.55)' : 'rgba(255, 255, 255, 0.08)')};
318
+ background: ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.14)' : 'rgba(255, 255, 255, 0.03)')};
319
+ color: ${({ $active }) => ($active ? '#f59e0b' : '#ccc')};
320
+ cursor: pointer;
321
+ display: flex;
322
+ align-items: center;
323
+ justify-content: center;
324
+ transition: color 0.15s, border-color 0.15s, background 0.15s;
325
+
326
+ &:hover {
327
+ color: #f59e0b;
328
+ border-color: rgba(245, 158, 11, 0.45);
329
+ }
330
+ `;
331
+
291
332
  const FiltersRow = styled.div`
292
333
  display: grid;
293
334
  grid-template-columns: 1fr 1fr;
@@ -302,7 +343,10 @@ const StyledDropdown = styled(Dropdown)`
302
343
  const ResultsWrapper = styled.div`
303
344
  position: relative;
304
345
  overflow-y: auto;
305
- height: 320px;
346
+ flex: 1;
347
+ min-height: 80px;
348
+ display: flex;
349
+ flex-direction: column;
306
350
  background: rgba(0, 0, 0, 0.2);
307
351
  border: 1px solid rgba(255, 255, 255, 0.05);
308
352
  border-radius: 4px;
@@ -333,17 +377,12 @@ const EmptyState = styled.div`
333
377
  display: flex;
334
378
  align-items: center;
335
379
  justify-content: center;
336
- height: 100%;
380
+ flex: 1;
337
381
  font-size: 0.55rem;
338
382
  color: #666;
339
383
  text-transform: uppercase;
340
384
  letter-spacing: 1px;
341
385
  `;
342
386
 
343
- const PagerContainer = styled.div`
344
- display: flex;
345
- justify-content: center;
346
- align-items: center;
347
- `;
348
387
 
349
388
  export { BLUEPRINTS_PER_PAGE };
@@ -42,9 +42,9 @@ export const BlueprintTable: React.FC<IBlueprintTableProps> = ({
42
42
  atlasJSON={atlasJSON}
43
43
  atlasIMG={atlasIMG}
44
44
  spriteKey={blueprint.texturePath || blueprint.key}
45
- width={32}
46
- height={32}
47
- imgScale={2}
45
+ width={24}
46
+ height={24}
47
+ imgScale={1.5}
48
48
  centered
49
49
  />
50
50
  </SpriteWrapper>
@@ -74,23 +74,31 @@ export const BlueprintTable: React.FC<IBlueprintTableProps> = ({
74
74
 
75
75
  const tableRowBase = `
76
76
  display: grid;
77
- grid-template-columns: 40px 1fr 120px 50px;
77
+ grid-template-columns: 28px 1fr 100px 40px;
78
78
  align-items: center;
79
- gap: 8px;
80
- padding: 6px 12px;
79
+ gap: 6px;
80
+ padding: 3px 8px;
81
+ `;
82
+
83
+ const mobileRowOverride = `
84
+ @media (max-width: 600px) {
85
+ padding: 1px 6px;
86
+ gap: 4px;
87
+ }
81
88
  `;
82
89
 
83
90
  const ResultsHeader = styled.div`
84
91
  ${tableRowBase}
92
+ ${mobileRowOverride}
85
93
  background: rgba(0, 0, 0, 0.4);
86
94
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
87
95
  position: sticky;
88
96
  top: 0;
89
97
  z-index: 1;
90
98
 
91
- > * {
92
- font-size: 0.45rem;
93
- color: #888;
99
+ && > * {
100
+ font-size: 0.48rem !important;
101
+ color: #555 !important;
94
102
  text-transform: uppercase;
95
103
  letter-spacing: 1px;
96
104
  }
@@ -98,6 +106,7 @@ const ResultsHeader = styled.div`
98
106
 
99
107
  const ResultRow = styled.div<{ $selectable: boolean }>`
100
108
  ${tableRowBase}
109
+ ${mobileRowOverride}
101
110
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
102
111
  cursor: ${p => p.$selectable ? 'pointer' : 'default'};
103
112
  transition: background 0.1s;
@@ -115,8 +124,8 @@ const SpriteWrapper = styled.div`
115
124
  display: flex;
116
125
  align-items: center;
117
126
  justify-content: center;
118
- width: 40px;
119
- height: 32px;
127
+ width: 28px;
128
+ height: 24px;
120
129
  `;
121
130
 
122
131
  const ColName = styled.div`
@@ -127,16 +136,16 @@ const ColName = styled.div`
127
136
  `;
128
137
 
129
138
  const BlueprintName = styled.span`
130
- font-size: 0.5rem;
131
- color: #ddd;
139
+ font-size: 0.6rem !important;
140
+ color: #e5e7eb !important;
132
141
  overflow: hidden;
133
142
  text-overflow: ellipsis;
134
143
  white-space: nowrap;
135
144
  `;
136
145
 
137
146
  const BlueprintMeta = styled.span`
138
- font-size: 0.4rem;
139
- color: #666;
147
+ font-size: 0.5rem !important;
148
+ color: #f59e0b !important;
140
149
  overflow: hidden;
141
150
  text-overflow: ellipsis;
142
151
  white-space: nowrap;
@@ -149,17 +158,17 @@ const ColType = styled.div`
149
158
  `;
150
159
 
151
160
  const TypeText = styled.span`
152
- font-size: 0.45rem;
153
- color: #aaa;
161
+ font-size: 0.52rem !important;
162
+ color: #9ca3af !important;
154
163
  `;
155
164
 
156
165
  const SubTypeText = styled.span`
157
- font-size: 0.4rem;
158
- color: #666;
166
+ font-size: 0.46rem !important;
167
+ color: #f59e0b !important;
159
168
  `;
160
169
 
161
170
  const ColTier = styled.div`
162
- font-size: 0.5rem;
163
- color: #f59e0b;
171
+ font-size: 0.6rem !important;
172
+ color: #f59e0b !important;
164
173
  text-align: center;
165
174
  `;
@@ -3,6 +3,7 @@ import { Delete } from 'pixelarticons/react/Delete';
3
3
  import { ShoppingBag } from 'pixelarticons/react/ShoppingBag';
4
4
  import React from 'react';
5
5
  import styled from 'styled-components';
6
+ import { GroupedRowContainer } from './GroupedRowContainer';
6
7
  import { uiColors } from '../../constants/uiColors';
7
8
  import { uiFonts } from '../../constants/uiFonts';
8
9
  import { resolveAtlasSpriteKey } from '../../utils/atlasUtils';
@@ -129,15 +130,6 @@ export const BuyOrderRow: React.FC<IBuyOrderRowProps> = ({
129
130
  {requestTagLabel}
130
131
  </LabelPill>
131
132
  )}
132
- {buyOrder.itemRarity && (
133
- <LabelPill
134
- background={rarityGlow ?? 'rgba(255,255,255,0.1)'}
135
- borderColor="rgba(255,255,255,0.08)"
136
- color="#fff"
137
- >
138
- {buyOrder.itemRarity}
139
- </LabelPill>
140
- )}
141
133
  {isOwn && (
142
134
  <LabelPill
143
135
  background={STATUS_COLORS[buyOrder.status]}
@@ -148,13 +140,15 @@ export const BuyOrderRow: React.FC<IBuyOrderRowProps> = ({
148
140
  </LabelPill>
149
141
  )}
150
142
  {timeRemaining && (
151
- <LabelPill
152
- background="rgba(255,255,255,0.08)"
153
- borderColor="rgba(255,255,255,0.08)"
154
- color="#fff"
155
- >
156
- {timeRemaining}
157
- </LabelPill>
143
+ <SimpleTooltip content="Expires in" direction="top">
144
+ <LabelPill
145
+ background="rgba(255,255,255,0.08)"
146
+ borderColor="rgba(255,255,255,0.08)"
147
+ color="#fff"
148
+ >
149
+ {timeRemaining}
150
+ </LabelPill>
151
+ </SimpleTooltip>
158
152
  )}
159
153
  </MetaRow>
160
154
  </ItemDetails>
@@ -185,6 +179,49 @@ export const BuyOrderRow: React.FC<IBuyOrderRowProps> = ({
185
179
  );
186
180
  };
187
181
 
182
+ export interface IGroupedBuyOrderRowProps {
183
+ bestOrder: IMarketplaceBuyOrderItem;
184
+ otherOrders: IMarketplaceBuyOrderItem[];
185
+ atlasJSON?: any;
186
+ atlasIMG?: any;
187
+ isOwn?: boolean;
188
+ onCancel?: (buyOrderId: string) => void;
189
+ onFulfill?: (buyOrderId: string) => void;
190
+ showRequestTag?: boolean;
191
+ }
192
+
193
+ export const GroupedBuyOrderRow: React.FC<IGroupedBuyOrderRowProps> = ({
194
+ bestOrder,
195
+ otherOrders,
196
+ atlasJSON,
197
+ atlasIMG,
198
+ isOwn,
199
+ onCancel,
200
+ onFulfill,
201
+ showRequestTag,
202
+ }) => {
203
+ const makeRow = (order: IMarketplaceBuyOrderItem) => (
204
+ <BuyOrderRow
205
+ key={order._id}
206
+ buyOrder={order}
207
+ atlasJSON={atlasJSON}
208
+ atlasIMG={atlasIMG}
209
+ isOwn={isOwn}
210
+ onCancel={onCancel}
211
+ onFulfill={onFulfill}
212
+ showRequestTag={showRequestTag}
213
+ />
214
+ );
215
+
216
+ return (
217
+ <GroupedRowContainer
218
+ mainRow={makeRow(bestOrder)}
219
+ subRows={otherOrders.map(makeRow)}
220
+ badgeLabel="requests"
221
+ />
222
+ );
223
+ };
224
+
188
225
  // ── Styled components matching MarketplaceRows layout ──
189
226
 
190
227
  const RowWrapper = styled.div`