@rpg-engine/long-bow 0.3.44 → 0.3.45
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/Abstractions/SlotsContainer.d.ts +1 -0
- package/dist/components/Button.d.ts +3 -2
- package/dist/components/Chat/Chat.d.ts +12 -2
- package/dist/components/Chatdeprecated/ChatDeprecated.d.ts +13 -0
- package/dist/components/CheckButton.d.ts +1 -2
- package/dist/components/CircularController/CircularController.d.ts +10 -0
- package/dist/components/CraftBook/CraftBook.d.ts +15 -0
- package/dist/components/CraftBook/MockItems.d.ts +2 -0
- package/dist/components/DraggableContainer.d.ts +1 -0
- package/dist/components/DropdownSelectorContainer.d.ts +13 -0
- package/dist/components/Equipment/EquipmentSet.d.ts +8 -0
- package/dist/components/Input.d.ts +1 -0
- package/dist/components/Item/Inventory/ItemContainer.d.ts +12 -0
- package/dist/components/Item/Inventory/ItemQuantitySelector.d.ts +7 -0
- package/dist/components/Item/Inventory/ItemSlot.d.ts +9 -0
- package/dist/components/NPCDialog/NPCDialogText.d.ts +4 -2
- package/dist/components/RadioInput/RadioButton.d.ts +8 -0
- package/dist/components/RadioInput/RadioInput.d.ts +13 -0
- package/dist/components/RadioInput/instruments.d.ts +4 -0
- package/dist/components/RangeSlider.d.ts +1 -0
- package/dist/components/Spellbook/QuickSpells.d.ts +10 -0
- package/dist/components/Spellbook/Spell.d.ts +11 -0
- package/dist/components/Spellbook/Spellbook.d.ts +15 -0
- package/dist/components/Spellbook/SpellbookShortcuts.d.ts +10 -0
- package/dist/components/Spellbook/constants.d.ts +3 -0
- package/dist/components/Spellbook/mockSpells.d.ts +2 -0
- package/dist/components/itemSelector/ItemSelector.d.ts +14 -0
- package/dist/constants/uiDevices.d.ts +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/long-bow.cjs.development.js +7464 -515
- 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 +7458 -518
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/mocks/equipmentSet.mocks.d.ts +15 -2
- package/dist/mocks/skills.mocks.d.ts +2 -121
- package/dist/stories/ChatDeprecated.stories.d.ts +5 -0
- package/dist/stories/CircullarController.stories.d.ts +5 -0
- package/dist/stories/CraftBook.stories.d.ts +4 -0
- package/dist/stories/DropdownSelectorContainer.stories.d.ts +5 -0
- package/dist/stories/ItemQuantitySelector.stories.d.ts +5 -0
- package/dist/stories/ItemSelector.stories.d.ts +4 -0
- package/dist/stories/QuickSpells.stories.d.ts +5 -0
- package/dist/stories/RadioInput.stories.d.ts +5 -0
- package/dist/stories/Spellbook.stories.d.ts +5 -0
- package/package.json +4 -1
- package/src/components/Abstractions/SlotsContainer.tsx +3 -0
- package/src/components/Button.tsx +18 -8
- package/src/components/Chat/Chat.tsx +105 -105
- package/src/components/Chatdeprecated/ChatDeprecated.tsx +200 -0
- package/src/components/CheckButton.tsx +1 -1
- package/src/components/CircularController/CircularController.tsx +162 -0
- package/src/components/CraftBook/CraftBook.tsx +230 -0
- package/src/components/CraftBook/MockItems.ts +46 -0
- package/src/components/DraggableContainer.tsx +4 -1
- package/src/components/Dropdown.tsx +7 -1
- package/src/components/DropdownSelectorContainer.tsx +42 -0
- package/src/components/Equipment/EquipmentSet.tsx +46 -0
- package/src/components/Input.tsx +6 -2
- package/src/components/Item/Inventory/ItemContainer.tsx +104 -6
- package/src/components/Item/Inventory/ItemQuantitySelector.tsx +142 -0
- package/src/components/Item/Inventory/ItemSlot.tsx +234 -34
- package/src/components/NPCDialog/NPCDialog.tsx +4 -28
- package/src/components/NPCDialog/NPCDialogText.tsx +75 -15
- package/src/components/NPCDialog/img/press-button.gif +0 -0
- package/src/components/RadioInput/RadioButton.tsx +98 -0
- package/src/components/RadioInput/RadioInput.tsx +99 -0
- package/src/components/RadioInput/instruments.ts +16 -0
- package/src/components/RangeSlider.tsx +37 -14
- package/src/components/SkillsContainer.tsx +1 -1
- package/src/components/Spellbook/QuickSpells.tsx +120 -0
- package/src/components/Spellbook/Spell.tsx +201 -0
- package/src/components/Spellbook/Spellbook.tsx +144 -0
- package/src/components/Spellbook/SpellbookShortcuts.tsx +77 -0
- package/src/components/Spellbook/constants.ts +12 -0
- package/src/components/Spellbook/mockSpells.ts +60 -0
- package/src/components/TimeWidget/TimeWidget.tsx +1 -0
- package/src/components/TradingMenu/TradingItemRow.tsx +43 -6
- package/src/components/TradingMenu/TradingMenu.tsx +1 -1
- package/src/components/itemSelector/ItemSelector.tsx +136 -0
- package/src/components/shared/SpriteFromAtlas.tsx +4 -1
- package/src/constants/uiDevices.ts +5 -0
- package/src/hooks/useOutsideAlerter.ts +2 -2
- package/src/index.tsx +8 -0
- package/src/mocks/atlas/items/items.json +6086 -314
- package/src/mocks/atlas/items/items.png +0 -0
- package/src/mocks/equipmentSet.mocks.ts +49 -4
- package/src/mocks/itemContainer.mocks.ts +54 -6
- package/src/mocks/skills.mocks.ts +8 -2
- package/src/stories/Chat.stories.tsx +20 -3
- package/src/stories/ChatDeprecated.stories.tsx +170 -0
- package/src/stories/CircullarController.stories.tsx +33 -0
- package/src/stories/CraftBook.stories.tsx +40 -0
- package/src/stories/DropdownSelectorContainer.stories.tsx +41 -0
- package/src/stories/EquipmentSet.stories.tsx +10 -0
- package/src/stories/ItemContainer.stories.tsx +84 -15
- package/src/stories/ItemQuantitySelector.stories.tsx +26 -0
- package/src/stories/ItemSelector.stories.tsx +77 -0
- package/src/stories/QuickSpells.stories.tsx +38 -0
- package/src/stories/RadioInput.stories.tsx +35 -0
- package/src/stories/RangeSlider.stories.tsx +10 -9
- package/src/stories/Spellbook.stories.tsx +107 -0
|
@@ -3,15 +3,18 @@ import {
|
|
|
3
3
|
IItem,
|
|
4
4
|
IItemContainer,
|
|
5
5
|
ItemContainerType,
|
|
6
|
+
ItemRarities,
|
|
6
7
|
ItemSlotType,
|
|
7
8
|
ItemType,
|
|
8
9
|
} from '@rpg-engine/shared';
|
|
9
10
|
|
|
10
11
|
import { observer } from 'mobx-react-lite';
|
|
11
|
-
import React, { useEffect, useState } from 'react';
|
|
12
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
13
|
+
import Draggable from 'react-draggable';
|
|
12
14
|
import styled from 'styled-components';
|
|
13
15
|
import { v4 as uuidv4 } from 'uuid';
|
|
14
16
|
import { uiFonts } from '../../../constants/uiFonts';
|
|
17
|
+
import { IPosition } from '../../../types/eventTypes';
|
|
15
18
|
import { RelativeListMenu } from '../../RelativeListMenu';
|
|
16
19
|
import { Ellipsis } from '../../shared/Ellipsis';
|
|
17
20
|
import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
|
|
@@ -52,6 +55,22 @@ interface IProps {
|
|
|
52
55
|
itemContainerType: ItemContainerType | null,
|
|
53
56
|
item: IItem
|
|
54
57
|
) => void;
|
|
58
|
+
onDragStart: (
|
|
59
|
+
item: IItem,
|
|
60
|
+
slotIndex: number,
|
|
61
|
+
itemContainerType: ItemContainerType | null
|
|
62
|
+
) => void;
|
|
63
|
+
onDragEnd: (quantity?: number) => void;
|
|
64
|
+
onOutsideDrop?: (item: IItem, position: IPosition) => void;
|
|
65
|
+
dragScale?: number;
|
|
66
|
+
checkIfItemCanBeMoved: () => boolean;
|
|
67
|
+
checkIfItemShouldDragEnd?: () => boolean;
|
|
68
|
+
openQuantitySelector?: (maxQuantity: number, callback: () => void) => void;
|
|
69
|
+
onPlaceDrop: (
|
|
70
|
+
item: IItem | null,
|
|
71
|
+
slotIndex: number,
|
|
72
|
+
itemContainerType: ItemContainerType | null
|
|
73
|
+
) => void;
|
|
55
74
|
atlasJSON: any;
|
|
56
75
|
atlasIMG: any;
|
|
57
76
|
isContextMenuDisabled?: boolean;
|
|
@@ -70,39 +89,57 @@ export const ItemSlot: React.FC<IProps> = observer(
|
|
|
70
89
|
atlasJSON,
|
|
71
90
|
atlasIMG,
|
|
72
91
|
isContextMenuDisabled = false,
|
|
92
|
+
onDragEnd,
|
|
93
|
+
onDragStart,
|
|
94
|
+
onPlaceDrop,
|
|
95
|
+
onOutsideDrop: onDrop,
|
|
96
|
+
checkIfItemCanBeMoved,
|
|
97
|
+
openQuantitySelector,
|
|
98
|
+
checkIfItemShouldDragEnd,
|
|
99
|
+
dragScale,
|
|
73
100
|
}) => {
|
|
74
101
|
const [isTooltipVisible, setTooltipVisible] = useState(false);
|
|
75
102
|
|
|
76
103
|
const [isContextMenuVisible, setIsContextMenuVisible] = useState(false);
|
|
77
104
|
|
|
105
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
106
|
+
const [wasDragged, setWasDragged] = useState(false);
|
|
107
|
+
const [dragPosition, setDragPosition] = useState<IPosition>({ x: 0, y: 0 });
|
|
108
|
+
const [dropPosition, setDropPosition] = useState<IPosition | null>(null);
|
|
109
|
+
const dragContainer = useRef<HTMLDivElement>(null);
|
|
110
|
+
|
|
78
111
|
const [contextActions, setContextActions] = useState<IContextMenuItem[]>(
|
|
79
112
|
[]
|
|
80
113
|
);
|
|
81
114
|
|
|
82
115
|
useEffect(() => {
|
|
116
|
+
setDragPosition({ x: 0, y: 0 });
|
|
117
|
+
setIsFocused(false);
|
|
118
|
+
|
|
83
119
|
if (item) {
|
|
84
120
|
setContextActions(generateContextMenu(item, containerType));
|
|
85
121
|
}
|
|
86
122
|
}, [item]);
|
|
87
123
|
|
|
88
|
-
|
|
89
|
-
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (onDrop && item && dropPosition) {
|
|
126
|
+
onDrop(item, dropPosition);
|
|
127
|
+
}
|
|
128
|
+
}, [dropPosition]);
|
|
90
129
|
|
|
130
|
+
const getStackInfo = (itemId: string, stackQty: number) => {
|
|
91
131
|
const isFractionalStackQty = stackQty % 1 !== 0;
|
|
92
132
|
const isLargerThan999 = stackQty > 999;
|
|
93
133
|
|
|
134
|
+
let qtyClassName = 'regular';
|
|
135
|
+
if (isLargerThan999) qtyClassName = 'small';
|
|
136
|
+
if (isFractionalStackQty) qtyClassName = 'xsmall';
|
|
137
|
+
|
|
94
138
|
if (stackQty > 1) {
|
|
95
139
|
return (
|
|
96
140
|
<ItemQtyContainer key={`qty-${itemId}`}>
|
|
97
141
|
<Ellipsis maxLines={1} maxWidth="48px">
|
|
98
|
-
<ItemQty
|
|
99
|
-
className={
|
|
100
|
-
isFractionalStackQty || isLargerThan999 ? 'small' : 'regular'
|
|
101
|
-
}
|
|
102
|
-
>
|
|
103
|
-
{' '}
|
|
104
|
-
{stackQty}{' '}
|
|
105
|
-
</ItemQty>
|
|
142
|
+
<ItemQty className={qtyClassName}> {stackQty} </ItemQty>
|
|
106
143
|
</Ellipsis>
|
|
107
144
|
</ItemQtyContainer>
|
|
108
145
|
);
|
|
@@ -112,6 +149,7 @@ export const ItemSlot: React.FC<IProps> = observer(
|
|
|
112
149
|
|
|
113
150
|
const renderItem = (itemToRender: IItem | null) => {
|
|
114
151
|
const element = [];
|
|
152
|
+
|
|
115
153
|
if (itemToRender?.texturePath) {
|
|
116
154
|
element.push(
|
|
117
155
|
<ErrorBoundary key={itemToRender._id}>
|
|
@@ -119,7 +157,14 @@ export const ItemSlot: React.FC<IProps> = observer(
|
|
|
119
157
|
key={itemToRender._id}
|
|
120
158
|
atlasIMG={atlasIMG}
|
|
121
159
|
atlasJSON={atlasJSON}
|
|
122
|
-
spriteKey={getItemTextureKeyPath(
|
|
160
|
+
spriteKey={getItemTextureKeyPath(
|
|
161
|
+
{
|
|
162
|
+
key: itemToRender.texturePath,
|
|
163
|
+
texturePath: itemToRender.texturePath,
|
|
164
|
+
stackQty: itemToRender.stackQty || 1,
|
|
165
|
+
},
|
|
166
|
+
atlasJSON
|
|
167
|
+
)}
|
|
123
168
|
imgScale={3}
|
|
124
169
|
/>
|
|
125
170
|
</ErrorBoundary>
|
|
@@ -142,13 +187,21 @@ export const ItemSlot: React.FC<IProps> = observer(
|
|
|
142
187
|
itemToRender.allowedEquipSlotType?.includes(slotSpriteMask!)
|
|
143
188
|
) {
|
|
144
189
|
const element = [];
|
|
190
|
+
|
|
145
191
|
element.push(
|
|
146
192
|
<ErrorBoundary key={itemToRender._id}>
|
|
147
193
|
<SpriteFromAtlas
|
|
148
194
|
key={itemToRender._id}
|
|
149
195
|
atlasIMG={atlasIMG}
|
|
150
196
|
atlasJSON={atlasJSON}
|
|
151
|
-
spriteKey={getItemTextureKeyPath(
|
|
197
|
+
spriteKey={getItemTextureKeyPath(
|
|
198
|
+
{
|
|
199
|
+
key: itemToRender.texturePath,
|
|
200
|
+
texturePath: itemToRender.texturePath,
|
|
201
|
+
stackQty: itemToRender.stackQty || 1,
|
|
202
|
+
},
|
|
203
|
+
atlasJSON
|
|
204
|
+
)}
|
|
152
205
|
imgScale={3}
|
|
153
206
|
/>
|
|
154
207
|
</ErrorBoundary>
|
|
@@ -189,29 +242,144 @@ export const ItemSlot: React.FC<IProps> = observer(
|
|
|
189
242
|
}
|
|
190
243
|
};
|
|
191
244
|
|
|
245
|
+
const resetItem = () => {
|
|
246
|
+
setTooltipVisible(false);
|
|
247
|
+
setWasDragged(false);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const onSuccesfulDrag = (quantity?: number) => {
|
|
251
|
+
resetItem();
|
|
252
|
+
|
|
253
|
+
if (quantity === -1) setDragPosition({ x: 0, y: 0 });
|
|
254
|
+
else if (item) {
|
|
255
|
+
onDragEnd(quantity);
|
|
256
|
+
resetItem();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
192
260
|
return (
|
|
193
261
|
<Container
|
|
262
|
+
item={item}
|
|
194
263
|
className="rpgui-icon empty-slot"
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
onMouseOut={() => {
|
|
199
|
-
if (onMouseOut) onMouseOut();
|
|
264
|
+
onMouseUp={() => {
|
|
265
|
+
const data = item ? item : null;
|
|
266
|
+
if (onPlaceDrop) onPlaceDrop(data, slotIndex, containerType);
|
|
200
267
|
}}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
setIsContextMenuVisible(!isContextMenuVisible);
|
|
209
|
-
}
|
|
268
|
+
onTouchEnd={e => {
|
|
269
|
+
const { clientX, clientY } = e.changedTouches[0];
|
|
270
|
+
const simulatedEvent = new MouseEvent('mouseup', {
|
|
271
|
+
clientX,
|
|
272
|
+
clientY,
|
|
273
|
+
bubbles: true,
|
|
274
|
+
});
|
|
210
275
|
|
|
211
|
-
|
|
212
|
-
|
|
276
|
+
document
|
|
277
|
+
.elementFromPoint(clientX, clientY)
|
|
278
|
+
?.dispatchEvent(simulatedEvent);
|
|
213
279
|
}}
|
|
214
280
|
>
|
|
281
|
+
<Draggable
|
|
282
|
+
defaultClassName={item ? 'draggable' : 'empty-slot'}
|
|
283
|
+
scale={dragScale}
|
|
284
|
+
onStop={(e, data) => {
|
|
285
|
+
if (wasDragged && item) {
|
|
286
|
+
//@ts-ignore
|
|
287
|
+
const classes: string[] = Array.from(e.target?.classList);
|
|
288
|
+
|
|
289
|
+
const isOutsideDrop =
|
|
290
|
+
classes.some(elm => {
|
|
291
|
+
return elm.includes('rpgui-content');
|
|
292
|
+
}) || classes.length === 0;
|
|
293
|
+
|
|
294
|
+
if (isOutsideDrop) {
|
|
295
|
+
setDropPosition({
|
|
296
|
+
x: data.x,
|
|
297
|
+
y: data.y,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
setWasDragged(false);
|
|
302
|
+
|
|
303
|
+
const target = dragContainer.current;
|
|
304
|
+
if (!target || !wasDragged) return;
|
|
305
|
+
|
|
306
|
+
const style = window.getComputedStyle(target);
|
|
307
|
+
const matrix = new DOMMatrixReadOnly(style.transform);
|
|
308
|
+
const x = matrix.m41;
|
|
309
|
+
const y = matrix.m42;
|
|
310
|
+
|
|
311
|
+
setDragPosition({ x, y });
|
|
312
|
+
|
|
313
|
+
setTimeout(() => {
|
|
314
|
+
if (checkIfItemCanBeMoved()) {
|
|
315
|
+
if (checkIfItemShouldDragEnd && !checkIfItemShouldDragEnd())
|
|
316
|
+
return;
|
|
317
|
+
|
|
318
|
+
if (
|
|
319
|
+
item.stackQty &&
|
|
320
|
+
item.stackQty !== 1 &&
|
|
321
|
+
openQuantitySelector
|
|
322
|
+
)
|
|
323
|
+
openQuantitySelector(item.stackQty, onSuccesfulDrag);
|
|
324
|
+
else onSuccesfulDrag(item.stackQty);
|
|
325
|
+
} else {
|
|
326
|
+
resetItem();
|
|
327
|
+
setIsFocused(false);
|
|
328
|
+
setDragPosition({ x: 0, y: 0 });
|
|
329
|
+
}
|
|
330
|
+
}, 100);
|
|
331
|
+
} else if (item) {
|
|
332
|
+
if (!isContextMenuDisabled)
|
|
333
|
+
setIsContextMenuVisible(!isContextMenuVisible);
|
|
334
|
+
|
|
335
|
+
onClick(item.type, containerType, item);
|
|
336
|
+
}
|
|
337
|
+
}}
|
|
338
|
+
onStart={() => {
|
|
339
|
+
if (!item) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (onDragStart) {
|
|
344
|
+
onDragStart(item, slotIndex, containerType);
|
|
345
|
+
}
|
|
346
|
+
}}
|
|
347
|
+
onDrag={(_e, data) => {
|
|
348
|
+
if (
|
|
349
|
+
Math.abs(data.x - dragPosition.x) > 5 ||
|
|
350
|
+
Math.abs(data.y - dragPosition.y) > 5
|
|
351
|
+
) {
|
|
352
|
+
setWasDragged(true);
|
|
353
|
+
setIsFocused(true);
|
|
354
|
+
}
|
|
355
|
+
}}
|
|
356
|
+
position={dragPosition}
|
|
357
|
+
cancel=".empty-slot"
|
|
358
|
+
>
|
|
359
|
+
<ItemContainer
|
|
360
|
+
ref={dragContainer}
|
|
361
|
+
isFocused={isFocused}
|
|
362
|
+
onMouseOver={event => {
|
|
363
|
+
onMouseOver(event, slotIndex, item, event.clientX, event.clientY);
|
|
364
|
+
}}
|
|
365
|
+
onMouseOut={() => {
|
|
366
|
+
if (onMouseOut) onMouseOut();
|
|
367
|
+
}}
|
|
368
|
+
onMouseEnter={() => {
|
|
369
|
+
setTooltipVisible(true);
|
|
370
|
+
}}
|
|
371
|
+
onMouseLeave={() => {
|
|
372
|
+
setTooltipVisible(false);
|
|
373
|
+
}}
|
|
374
|
+
>
|
|
375
|
+
{onRenderSlot(item)}
|
|
376
|
+
</ItemContainer>
|
|
377
|
+
</Draggable>
|
|
378
|
+
|
|
379
|
+
{isTooltipVisible && item && !isFocused && (
|
|
380
|
+
<ItemTooltip label={item.name} />
|
|
381
|
+
)}
|
|
382
|
+
|
|
215
383
|
{!isContextMenuDisabled && isContextMenuVisible && contextActions && (
|
|
216
384
|
<RelativeListMenu
|
|
217
385
|
options={contextActions}
|
|
@@ -226,31 +394,60 @@ export const ItemSlot: React.FC<IProps> = observer(
|
|
|
226
394
|
}}
|
|
227
395
|
/>
|
|
228
396
|
)}
|
|
229
|
-
|
|
230
|
-
{isTooltipVisible && item && <ItemTooltip label={item.name} />}
|
|
231
|
-
|
|
232
|
-
{onRenderSlot(item)}
|
|
233
397
|
</Container>
|
|
234
398
|
);
|
|
235
399
|
}
|
|
236
400
|
);
|
|
237
401
|
|
|
238
|
-
const
|
|
402
|
+
const rarityColor = (item: IItem | null) => {
|
|
403
|
+
switch (item?.rarity) {
|
|
404
|
+
case ItemRarities.Uncommon:
|
|
405
|
+
return 'rgba(13, 193, 13, 0.6)';
|
|
406
|
+
case ItemRarities.Rare:
|
|
407
|
+
return 'rgba(8, 104, 187, 0.6)';
|
|
408
|
+
case ItemRarities.Epic:
|
|
409
|
+
return 'rgba(191, 0, 255, 0.6)';
|
|
410
|
+
case ItemRarities.Legendary:
|
|
411
|
+
return 'rgba(255, 191, 0,0.6)';
|
|
412
|
+
default:
|
|
413
|
+
return 'unset';
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
interface ContainerTypes {
|
|
418
|
+
item: IItem | null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const Container = styled.div<ContainerTypes>`
|
|
239
422
|
margin: 0.1rem;
|
|
240
423
|
.sprite-from-atlas-img {
|
|
241
424
|
position: relative;
|
|
242
425
|
top: 1.5rem;
|
|
243
426
|
left: 1.5rem;
|
|
427
|
+
border-color: ${({ item }) => rarityColor(item)};
|
|
428
|
+
box-shadow: ${({ item }) => `0 0 5px 2px ${rarityColor(item)}`} inset, ${({
|
|
429
|
+
item,
|
|
430
|
+
}) => `0 0 4px 3px ${rarityColor(item)}`};
|
|
431
|
+
//background-color: ${({ item }) => rarityColor(item)};
|
|
244
432
|
}
|
|
245
433
|
position: relative;
|
|
246
434
|
`;
|
|
247
435
|
|
|
436
|
+
const ItemContainer = styled.div<{ isFocused?: boolean }>`
|
|
437
|
+
width: 100%;
|
|
438
|
+
height: 100%;
|
|
439
|
+
position: relative;
|
|
440
|
+
|
|
441
|
+
${props => props.isFocused && 'z-index: 100; pointer-events: none;'}
|
|
442
|
+
`;
|
|
443
|
+
|
|
248
444
|
const ItemQtyContainer = styled.div`
|
|
249
445
|
position: relative;
|
|
250
446
|
width: 85%;
|
|
251
447
|
height: 16px;
|
|
252
448
|
top: 25px;
|
|
253
449
|
left: 2px;
|
|
450
|
+
pointer-events: none;
|
|
254
451
|
|
|
255
452
|
display: flex;
|
|
256
453
|
justify-content: flex-end;
|
|
@@ -263,4 +460,7 @@ const ItemQty = styled.span`
|
|
|
263
460
|
&.small {
|
|
264
461
|
font-size: ${uiFonts.size.xsmall};
|
|
265
462
|
}
|
|
463
|
+
&.xsmall {
|
|
464
|
+
font-size: ${uiFonts.size.xxsmall};
|
|
465
|
+
}
|
|
266
466
|
`;
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import { RPGUIContainer, RPGUIContainerTypes } from '../RPGUIContainer';
|
|
4
|
-
import aliceDefaultThumbnail from './img/npcDialog/npcThumbnails/alice.png';
|
|
5
|
-
import pressSpaceGif from './img/space.gif';
|
|
6
4
|
import { NPCDialogText } from './NPCDialogText';
|
|
7
5
|
import {
|
|
8
6
|
IQuestionDialog,
|
|
9
7
|
IQuestionDialogAnswer,
|
|
10
8
|
QuestionDialog,
|
|
11
9
|
} from './QuestionDialog/QuestionDialog';
|
|
10
|
+
import aliceDefaultThumbnail from './img/npcDialog/npcThumbnails/alice.png';
|
|
12
11
|
|
|
13
12
|
export enum NPCDialogType {
|
|
14
13
|
TextOnly = 'TextOnly',
|
|
@@ -34,14 +33,10 @@ export const NPCDialog: React.FC<INPCDialogProps> = ({
|
|
|
34
33
|
questions,
|
|
35
34
|
answers,
|
|
36
35
|
}) => {
|
|
37
|
-
const [showGoNextIndicator, setShowGoNextIndicator] = useState<boolean>(
|
|
38
|
-
false
|
|
39
|
-
);
|
|
40
|
-
|
|
41
36
|
return (
|
|
42
37
|
<RPGUIContainer
|
|
43
38
|
type={RPGUIContainerTypes.FramedGold}
|
|
44
|
-
width={isQuestionDialog ? '600px' : '
|
|
39
|
+
width={isQuestionDialog ? '600px' : '80%'}
|
|
45
40
|
height={'180px'}
|
|
46
41
|
>
|
|
47
42
|
{isQuestionDialog && questions && answers ? (
|
|
@@ -72,8 +67,7 @@ export const NPCDialog: React.FC<INPCDialogProps> = ({
|
|
|
72
67
|
flex={type === NPCDialogType.TextAndThumbnail ? '70%' : '100%'}
|
|
73
68
|
>
|
|
74
69
|
<NPCDialogText
|
|
75
|
-
|
|
76
|
-
onEndStep={() => setShowGoNextIndicator(true)}
|
|
70
|
+
type={type}
|
|
77
71
|
text={text || 'No text provided.'}
|
|
78
72
|
onClose={() => {
|
|
79
73
|
if (onClose) {
|
|
@@ -88,12 +82,6 @@ export const NPCDialog: React.FC<INPCDialogProps> = ({
|
|
|
88
82
|
</ThumbnailContainer>
|
|
89
83
|
)}
|
|
90
84
|
</Container>
|
|
91
|
-
{showGoNextIndicator && (
|
|
92
|
-
<PressSpaceIndicator
|
|
93
|
-
right={type === NPCDialogType.TextOnly ? '1rem' : '10.5rem'}
|
|
94
|
-
src={pressSpaceGif}
|
|
95
|
-
/>
|
|
96
|
-
)}
|
|
97
85
|
</>
|
|
98
86
|
)}
|
|
99
87
|
</RPGUIContainer>
|
|
@@ -131,15 +119,3 @@ const NPCThumbnail = styled.img`
|
|
|
131
119
|
height: 128px;
|
|
132
120
|
width: 128px;
|
|
133
121
|
`;
|
|
134
|
-
|
|
135
|
-
interface IPressSpaceIndicatorProps {
|
|
136
|
-
right: string;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const PressSpaceIndicator = styled.img<IPressSpaceIndicatorProps>`
|
|
140
|
-
position: absolute;
|
|
141
|
-
right: ${({ right }) => right};
|
|
142
|
-
bottom: 1rem;
|
|
143
|
-
height: 20.7px;
|
|
144
|
-
image-rendering: -webkit-optimize-contrast;
|
|
145
|
-
`;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
|
+
import { NPCDialogType } from '../..';
|
|
4
|
+
import { IS_MOBILE_OR_TABLET } from '../../constants/uiDevices';
|
|
3
5
|
import { chunkString } from '../../libs/StringHelpers';
|
|
4
6
|
import { DynamicText } from '../typography/DynamicText';
|
|
7
|
+
import pressButtonGif from './img/press-button.gif';
|
|
8
|
+
import pressSpaceGif from './img/space.gif';
|
|
5
9
|
|
|
6
10
|
interface IProps {
|
|
7
11
|
text: string;
|
|
8
12
|
onClose: () => void;
|
|
9
|
-
onEndStep
|
|
10
|
-
onStartStep
|
|
13
|
+
onEndStep?: () => void;
|
|
14
|
+
onStartStep?: () => void;
|
|
15
|
+
type?: NPCDialogType;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
export const NPCDialogText: React.FC<IProps> = ({
|
|
@@ -15,21 +20,43 @@ export const NPCDialogText: React.FC<IProps> = ({
|
|
|
15
20
|
onClose,
|
|
16
21
|
onEndStep,
|
|
17
22
|
onStartStep,
|
|
23
|
+
type,
|
|
18
24
|
}) => {
|
|
19
|
-
const
|
|
25
|
+
const windowSize = useRef([window.innerWidth, window.innerHeight]);
|
|
26
|
+
function maxCharacters(width: number) {
|
|
27
|
+
// Set the font size to 16 pixels
|
|
28
|
+
var fontSize = 11.2;
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
// Calculate the number of characters that can fit in one line
|
|
31
|
+
var charactersPerLine = Math.floor(width / 2 / fontSize);
|
|
32
|
+
|
|
33
|
+
// Calculate the number of lines that can fit in the div
|
|
34
|
+
var linesPerDiv = Math.floor(180 / fontSize);
|
|
35
|
+
|
|
36
|
+
// Calculate the maximum number of characters that can fit in the div
|
|
37
|
+
var maxCharacters = charactersPerLine * linesPerDiv;
|
|
38
|
+
|
|
39
|
+
// Return the maximum number of characters
|
|
40
|
+
return Math.round(maxCharacters / 5);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const textChunks = chunkString(text, maxCharacters(windowSize.current[0]));
|
|
22
44
|
|
|
45
|
+
const [chunkIndex, setChunkIndex] = useState<number>(0);
|
|
23
46
|
const onHandleSpacePress = (event: KeyboardEvent) => {
|
|
24
47
|
if (event.code === 'Space') {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
goToNextStep();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const goToNextStep = () => {
|
|
53
|
+
const hasNextChunk = textChunks?.[chunkIndex + 1] || false;
|
|
54
|
+
|
|
55
|
+
if (hasNextChunk) {
|
|
56
|
+
setChunkIndex(prev => prev + 1);
|
|
57
|
+
} else {
|
|
58
|
+
// if there's no more text chunks, close the dialog
|
|
59
|
+
onClose();
|
|
33
60
|
}
|
|
34
61
|
};
|
|
35
62
|
|
|
@@ -39,15 +66,48 @@ export const NPCDialogText: React.FC<IProps> = ({
|
|
|
39
66
|
return () => document.removeEventListener('keydown', onHandleSpacePress);
|
|
40
67
|
}, [chunkIndex]);
|
|
41
68
|
|
|
69
|
+
const [showGoNextIndicator, setShowGoNextIndicator] = useState<boolean>(
|
|
70
|
+
false
|
|
71
|
+
);
|
|
72
|
+
|
|
42
73
|
return (
|
|
43
74
|
<Container>
|
|
44
75
|
<DynamicText
|
|
45
76
|
text={textChunks?.[chunkIndex] || ''}
|
|
46
|
-
onFinish={
|
|
47
|
-
|
|
77
|
+
onFinish={() => {
|
|
78
|
+
setShowGoNextIndicator(true);
|
|
79
|
+
|
|
80
|
+
onEndStep && onEndStep();
|
|
81
|
+
}}
|
|
82
|
+
onStart={() => {
|
|
83
|
+
setShowGoNextIndicator(false);
|
|
84
|
+
|
|
85
|
+
onStartStep && onStartStep();
|
|
86
|
+
}}
|
|
48
87
|
/>
|
|
88
|
+
{showGoNextIndicator && (
|
|
89
|
+
<PressSpaceIndicator
|
|
90
|
+
right={type === NPCDialogType.TextOnly ? '1rem' : '10.5rem'}
|
|
91
|
+
src={IS_MOBILE_OR_TABLET ? pressButtonGif : pressSpaceGif}
|
|
92
|
+
onClick={() => {
|
|
93
|
+
goToNextStep();
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
49
97
|
</Container>
|
|
50
98
|
);
|
|
51
99
|
};
|
|
52
100
|
|
|
53
101
|
const Container = styled.div``;
|
|
102
|
+
|
|
103
|
+
interface IPressSpaceIndicatorProps {
|
|
104
|
+
right: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const PressSpaceIndicator = styled.img<IPressSpaceIndicatorProps>`
|
|
108
|
+
position: absolute;
|
|
109
|
+
right: ${({ right }) => right};
|
|
110
|
+
bottom: 1rem;
|
|
111
|
+
height: 20.7px;
|
|
112
|
+
image-rendering: -webkit-optimize-contrast;
|
|
113
|
+
`;
|
|
Binary file
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { capitalize } from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { uiColors } from '../../constants/uiColors';
|
|
5
|
+
import { Ellipsis } from '../shared/Ellipsis';
|
|
6
|
+
|
|
7
|
+
export interface IRadioProps {
|
|
8
|
+
title:string,
|
|
9
|
+
subtitle:string,
|
|
10
|
+
instrument:string|null
|
|
11
|
+
setInstrument: (value: string) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const RadioButton: React.FC<IRadioProps> = ({
|
|
15
|
+
title,
|
|
16
|
+
subtitle,
|
|
17
|
+
instrument,
|
|
18
|
+
setInstrument,
|
|
19
|
+
}) => {
|
|
20
|
+
const isRadioSelected=(value:string):boolean=>instrument===value;
|
|
21
|
+
|
|
22
|
+
const handleClick = (e:string):void => {
|
|
23
|
+
setInstrument(e)
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<ItemWrapper onClick={() => handleClick(title)} >
|
|
28
|
+
<ItemIconContainer>
|
|
29
|
+
<SpriteContainer id='elemento-radio'>
|
|
30
|
+
<input
|
|
31
|
+
type='radio'
|
|
32
|
+
name='react-radio-btn'
|
|
33
|
+
className='rpgui-radio'
|
|
34
|
+
value={title}
|
|
35
|
+
checked={isRadioSelected(title)}
|
|
36
|
+
onChange={(e)=>(e)}
|
|
37
|
+
/>
|
|
38
|
+
</SpriteContainer>
|
|
39
|
+
</ItemIconContainer>
|
|
40
|
+
<ItemNameContainer>
|
|
41
|
+
<NameValue onClick={() => handleClick(title)}>
|
|
42
|
+
<p>{title}</p>
|
|
43
|
+
<p>
|
|
44
|
+
<Ellipsis maxLines={1} maxWidth="250px">
|
|
45
|
+
{capitalize(subtitle)}
|
|
46
|
+
</Ellipsis>
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
</NameValue>
|
|
50
|
+
</ItemNameContainer>
|
|
51
|
+
</ItemWrapper>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const ItemWrapper = styled.div`
|
|
56
|
+
width: 100%;
|
|
57
|
+
margin: auto;
|
|
58
|
+
display: flex;
|
|
59
|
+
justify-content: space-between;
|
|
60
|
+
margin-bottom: 1rem;
|
|
61
|
+
|
|
62
|
+
&:hover {
|
|
63
|
+
background-color: ${uiColors.darkGray};
|
|
64
|
+
}
|
|
65
|
+
padding: 0.5rem;
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
const ItemNameContainer = styled.div`
|
|
70
|
+
flex: 80%;
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const ItemIconContainer = styled.div`
|
|
74
|
+
display: flex;
|
|
75
|
+
justify-content: flex-start;
|
|
76
|
+
align-items: center;
|
|
77
|
+
|
|
78
|
+
flex: 0 0 58px;
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
const SpriteContainer = styled.div`
|
|
82
|
+
input {
|
|
83
|
+
display:block !important;
|
|
84
|
+
&:hover {
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
position: relative;
|
|
89
|
+
top: -0.5rem;
|
|
90
|
+
left: 0.5rem;
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
const NameValue = styled.div`
|
|
94
|
+
p {
|
|
95
|
+
font-size: 0.75rem;
|
|
96
|
+
margin: 0;
|
|
97
|
+
}
|
|
98
|
+
`;
|