@rpg-engine/long-bow 0.2.90 → 0.2.92

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.
Files changed (38) hide show
  1. package/dist/components/Abstractions/SlotsContainer.d.ts +1 -0
  2. package/dist/components/DraggableContainer.d.ts +1 -0
  3. package/dist/components/Equipment/EquipmentSet.d.ts +6 -0
  4. package/dist/components/Input.d.ts +1 -0
  5. package/dist/components/Item/Inventory/ItemContainer.d.ts +10 -0
  6. package/dist/components/Item/Inventory/ItemQuantitySelector.d.ts +7 -0
  7. package/dist/components/Item/Inventory/ItemSlot.d.ts +7 -0
  8. package/dist/components/RangeSlider.d.ts +1 -0
  9. package/dist/index.d.ts +4 -5
  10. package/dist/long-bow.cjs.development.js +523 -220
  11. package/dist/long-bow.cjs.development.js.map +1 -1
  12. package/dist/long-bow.cjs.production.min.js +1 -1
  13. package/dist/long-bow.cjs.production.min.js.map +1 -1
  14. package/dist/long-bow.esm.js +524 -220
  15. package/dist/long-bow.esm.js.map +1 -1
  16. package/dist/stories/ItemQuantitySelector.stories.d.ts +5 -0
  17. package/package.json +1 -1
  18. package/src/components/Abstractions/SlotsContainer.tsx +3 -0
  19. package/src/components/CraftBook/CraftBook.tsx +82 -92
  20. package/src/components/CraftBook/MockItems.ts +23 -20
  21. package/src/components/DraggableContainer.tsx +3 -0
  22. package/src/components/Equipment/EquipmentSet.tsx +40 -0
  23. package/src/components/Input.tsx +6 -2
  24. package/src/components/Item/Inventory/ItemContainer.tsx +98 -6
  25. package/src/components/Item/Inventory/ItemQuantitySelector.tsx +142 -0
  26. package/src/components/Item/Inventory/ItemSlot.tsx +171 -24
  27. package/src/components/RangeSlider.tsx +37 -14
  28. package/src/index.tsx +4 -5
  29. package/src/mocks/itemContainer.mocks.ts +1 -1
  30. package/src/stories/EquipmentSet.stories.tsx +10 -0
  31. package/src/stories/ItemContainer.stories.tsx +83 -15
  32. package/src/stories/ItemQuantitySelector.stories.tsx +26 -0
  33. package/src/stories/RangeSlider.stories.tsx +10 -9
  34. package/src/.DS_Store +0 -0
  35. package/src/components/NPCDialog/.DS_Store +0 -0
  36. package/src/components/NPCDialog/img/.DS_Store +0 -0
  37. package/src/mocks/.DS_Store +0 -0
  38. package/src/mocks/atlas/.DS_Store +0 -0
@@ -0,0 +1,142 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import styled from 'styled-components';
3
+ import { Button, ButtonTypes } from '../../Button';
4
+ import { Input } from '../../Input';
5
+ import { RPGUIContainer, RPGUIContainerTypes } from '../../RPGUIContainer';
6
+ import { RangeSlider, RangeSliderType } from '../../RangeSlider';
7
+
8
+ export interface IItemQuantitySelectorProps {
9
+ quantity: number;
10
+ onConfirm: (quantity: number) => void;
11
+ onClose: () => void;
12
+ }
13
+
14
+ export const ItemQuantitySelector: React.FC<IItemQuantitySelectorProps> = ({
15
+ quantity,
16
+ onConfirm,
17
+ onClose,
18
+ }) => {
19
+ const [value, setValue] = useState(quantity);
20
+
21
+ const inputRef = useRef<HTMLInputElement>(null);
22
+
23
+ useEffect(() => {
24
+ if (inputRef.current) {
25
+ inputRef.current.focus();
26
+ inputRef.current.select();
27
+
28
+ const closeSelector = (e: KeyboardEvent) => {
29
+ if (e.key === 'Escape') {
30
+ onClose();
31
+ }
32
+ };
33
+
34
+ document.addEventListener('keydown', closeSelector);
35
+
36
+ return () => {
37
+ document.removeEventListener('keydown', closeSelector);
38
+ };
39
+ }
40
+
41
+ return () => {};
42
+ }, []);
43
+
44
+ return (
45
+ <StyledContainer type={RPGUIContainerTypes.Framed} width="25rem">
46
+ <CloseButton
47
+ className="container-close"
48
+ onClick={onClose}
49
+ onTouchStart={onClose}
50
+ >
51
+ X
52
+ </CloseButton>
53
+ <h2>Select quantity to move</h2>
54
+ <StyledForm
55
+ style={{ width: '100%' }}
56
+ onSubmit={e => {
57
+ e.preventDefault();
58
+
59
+ const numberValue = Number(value);
60
+
61
+ if (Number.isNaN(numberValue)) {
62
+ return;
63
+ }
64
+
65
+ onConfirm(Math.max(1, Math.min(quantity, numberValue)));
66
+ }}
67
+ noValidate
68
+ >
69
+ <StyledInput
70
+ innerRef={inputRef}
71
+ placeholder="Enter quantity"
72
+ type="number"
73
+ min={1}
74
+ max={quantity}
75
+ value={value}
76
+ onChange={e => {
77
+ if (Number(e.target.value) >= quantity) {
78
+ setValue(quantity);
79
+ return;
80
+ }
81
+
82
+ setValue((e.target.value as unknown) as number);
83
+ }}
84
+ onBlur={e => {
85
+ const newValue = Math.max(
86
+ 1,
87
+ Math.min(quantity, Number(e.target.value))
88
+ );
89
+
90
+ setValue(newValue);
91
+ }}
92
+ />
93
+ <RangeSlider
94
+ type={RangeSliderType.Slider}
95
+ valueMin={1}
96
+ valueMax={quantity}
97
+ width="100%"
98
+ onChange={setValue}
99
+ value={value}
100
+ />
101
+ <Button buttonType={ButtonTypes.RPGUIButton} type="submit">
102
+ Confirm
103
+ </Button>
104
+ </StyledForm>
105
+ </StyledContainer>
106
+ );
107
+ };
108
+
109
+ const StyledContainer = styled(RPGUIContainer)`
110
+ display: flex;
111
+ flex-direction: column;
112
+ align-items: center;
113
+ `;
114
+
115
+ const StyledForm = styled.form`
116
+ display: flex;
117
+ flex-direction: column;
118
+ align-items: center;
119
+ width: 100%;
120
+ `;
121
+ const StyledInput = styled(Input)`
122
+ text-align: center;
123
+
124
+ &::-webkit-outer-spin-button,
125
+ &::-webkit-inner-spin-button {
126
+ -webkit-appearance: none;
127
+ margin: 0;
128
+ }
129
+
130
+ &[type='number'] {
131
+ -moz-appearance: textfield;
132
+ }
133
+ `;
134
+
135
+ const CloseButton = styled.div`
136
+ position: absolute;
137
+ top: 3px;
138
+ right: 0px;
139
+ color: white;
140
+ z-index: 22;
141
+ font-size: 0.8rem;
142
+ `;
@@ -4,14 +4,16 @@ import {
4
4
  IItemContainer,
5
5
  ItemContainerType,
6
6
  ItemSlotType,
7
- ItemType
7
+ ItemType,
8
8
  } from '@rpg-engine/shared';
9
9
 
10
10
  import { observer } from 'mobx-react-lite';
11
- import React, { useEffect, useState } from 'react';
11
+ import React, { useEffect, useRef, useState } from 'react';
12
+ import Draggable from 'react-draggable';
12
13
  import styled from 'styled-components';
13
14
  import { v4 as uuidv4 } from 'uuid';
14
15
  import { uiFonts } from '../../../constants/uiFonts';
16
+ import { IPosition } from '../../../types/eventTypes';
15
17
  import { RelativeListMenu } from '../../RelativeListMenu';
16
18
  import { Ellipsis } from '../../shared/Ellipsis';
17
19
  import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
@@ -52,6 +54,20 @@ interface IProps {
52
54
  itemContainerType: ItemContainerType | null,
53
55
  item: IItem
54
56
  ) => void;
57
+ onDragStart: (
58
+ item: IItem,
59
+ slotIndex: number,
60
+ itemContainerType: ItemContainerType | null
61
+ ) => void;
62
+ onDragEnd: (quantity?: number) => void;
63
+ onOutsideDrop?: (item: IItem, position: IPosition) => void;
64
+ checkIfItemCanBeMoved: () => boolean;
65
+ openQuantitySelector?: (maxQuantity: number, callback: () => void) => void;
66
+ onPlaceDrop: (
67
+ item: IItem | null,
68
+ slotIndex: number,
69
+ itemContainerType: ItemContainerType | null
70
+ ) => void;
55
71
  atlasJSON: any;
56
72
  atlasIMG: any;
57
73
  isContextMenuDisabled?: boolean;
@@ -70,21 +86,41 @@ export const ItemSlot: React.FC<IProps> = observer(
70
86
  atlasJSON,
71
87
  atlasIMG,
72
88
  isContextMenuDisabled = false,
89
+ onDragEnd,
90
+ onDragStart,
91
+ onPlaceDrop,
92
+ onOutsideDrop: onDrop,
93
+ checkIfItemCanBeMoved,
94
+ openQuantitySelector,
73
95
  }) => {
74
96
  const [isTooltipVisible, setTooltipVisible] = useState(false);
75
97
 
76
98
  const [isContextMenuVisible, setIsContextMenuVisible] = useState(false);
77
99
 
100
+ const [isFocused, setIsFocused] = useState(false);
101
+ const [wasDragged, setWasDragged] = useState(false);
102
+ const [dragPosition, setDragPosition] = useState<IPosition>({ x: 0, y: 0 });
103
+ const [dropPosition, setDropPosition] = useState<IPosition | null>(null);
104
+ const dragContainer = useRef<HTMLDivElement>(null);
105
+
78
106
  const [contextActions, setContextActions] = useState<IContextMenuItem[]>(
79
107
  []
80
108
  );
81
109
 
82
110
  useEffect(() => {
111
+ setDragPosition({ x: 0, y: 0 });
112
+
83
113
  if (item) {
84
114
  setContextActions(generateContextMenu(item, containerType));
85
115
  }
86
116
  }, [item]);
87
117
 
118
+ useEffect(() => {
119
+ if (onDrop && item && dropPosition) {
120
+ onDrop(item, dropPosition);
121
+ }
122
+ }, [dropPosition]);
123
+
88
124
  const getStackInfo = (itemId: string, stackQty: number) => {
89
125
  // if (itemToRender?.isStackable && itemToRender?.stackQty) {
90
126
 
@@ -112,7 +148,7 @@ export const ItemSlot: React.FC<IProps> = observer(
112
148
 
113
149
  const renderItem = (itemToRender: IItem | null) => {
114
150
  const element = [];
115
-
151
+
116
152
  if (itemToRender?.texturePath) {
117
153
  element.push(
118
154
  <ErrorBoundary key={itemToRender._id}>
@@ -150,7 +186,7 @@ export const ItemSlot: React.FC<IProps> = observer(
150
186
  itemToRender.allowedEquipSlotType?.includes(slotSpriteMask!)
151
187
  ) {
152
188
  const element = [];
153
-
189
+
154
190
  element.push(
155
191
  <ErrorBoundary key={itemToRender._id}>
156
192
  <SpriteFromAtlas
@@ -205,29 +241,135 @@ export const ItemSlot: React.FC<IProps> = observer(
205
241
  }
206
242
  };
207
243
 
244
+ const resetItem = () => {
245
+ setTooltipVisible(false);
246
+ setIsFocused(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
+
208
260
  return (
209
261
  <Container
210
262
  className="rpgui-icon empty-slot"
211
- onMouseOver={event =>
212
- onMouseOver(event, slotIndex, item, event.clientX, event.clientY)
213
- }
214
- onMouseOut={() => {
215
- if (onMouseOut) onMouseOut();
263
+ onMouseUp={() => {
264
+ const data = item ? item : null;
265
+ if (onPlaceDrop) onPlaceDrop(data, slotIndex, containerType);
216
266
  }}
217
- onMouseEnter={() => setTooltipVisible(true)}
218
- onMouseLeave={() => setTooltipVisible(false)}
219
- onClick={() => {
220
- setTooltipVisible(false);
221
-
222
- if (item) {
223
- if (!isContextMenuDisabled) {
224
- setIsContextMenuVisible(!isContextMenuVisible);
225
- }
267
+ onTouchEnd={e => {
268
+ const { clientX, clientY } = e.changedTouches[0];
269
+ const simulatedEvent = new MouseEvent('mouseup', {
270
+ clientX,
271
+ clientY,
272
+ bubbles: true,
273
+ });
226
274
 
227
- onClick(item.type, containerType, item);
228
- }
275
+ document
276
+ .elementFromPoint(clientX, clientY)
277
+ ?.dispatchEvent(simulatedEvent);
229
278
  }}
230
279
  >
280
+ <Draggable
281
+ defaultClassName={item ? 'draggable' : 'empty-slot'}
282
+ onStop={(e, data) => {
283
+ if (wasDragged && item) {
284
+ //@ts-ignore
285
+ const classes: string[] = Array.from(e.target?.classList);
286
+
287
+ console.log(classes);
288
+
289
+ const isOutsideDrop = classes.some(elm => {
290
+ //elm matches ItemContainer string
291
+
292
+ return elm.includes('rpgui-content');
293
+ });
294
+
295
+ if (isOutsideDrop) {
296
+ setDropPosition({
297
+ x: data.x,
298
+ y: data.y,
299
+ });
300
+ }
301
+
302
+ setWasDragged(false);
303
+
304
+ const target = dragContainer.current;
305
+ if (!target || !wasDragged) return;
306
+
307
+ const style = window.getComputedStyle(target);
308
+ const matrix = new DOMMatrixReadOnly(style.transform);
309
+ const x = matrix.m41;
310
+ const y = matrix.m42;
311
+
312
+ setDragPosition({ x, y });
313
+
314
+ setTimeout(() => {
315
+ if (checkIfItemCanBeMoved()) {
316
+ if (
317
+ item.stackQty &&
318
+ item.stackQty !== 1 &&
319
+ openQuantitySelector
320
+ )
321
+ openQuantitySelector(item.stackQty, onSuccesfulDrag);
322
+ else onSuccesfulDrag(item.stackQty);
323
+ } else {
324
+ resetItem();
325
+ setDragPosition({ x: 0, y: 0 });
326
+ }
327
+ }, 100);
328
+ } else if (item) {
329
+ if (!isContextMenuDisabled)
330
+ setIsContextMenuVisible(!isContextMenuVisible);
331
+
332
+ onClick(item.type, containerType, item);
333
+ }
334
+ }}
335
+ onStart={() => {
336
+ if (!item) {
337
+ return;
338
+ }
339
+
340
+ if (onDragStart) {
341
+ onDragStart(item, slotIndex, containerType);
342
+ }
343
+ }}
344
+ onDrag={() => {
345
+ setWasDragged(true);
346
+ setIsFocused(true);
347
+ }}
348
+ position={dragPosition}
349
+ cancel=".empty-slot"
350
+ >
351
+ <ItemContainer
352
+ ref={dragContainer}
353
+ isFocused={isFocused}
354
+ onMouseOver={event => {
355
+ onMouseOver(event, slotIndex, item, event.clientX, event.clientY);
356
+ }}
357
+ onMouseOut={() => {
358
+ if (onMouseOut) onMouseOut();
359
+ }}
360
+ onMouseEnter={() => {
361
+ setTooltipVisible(true);
362
+ }}
363
+ onMouseLeave={() => {
364
+ setTooltipVisible(false);
365
+ }}
366
+ >
367
+ {onRenderSlot(item)}
368
+ </ItemContainer>
369
+ </Draggable>
370
+
371
+ {isTooltipVisible && item && <ItemTooltip label={item.name} />}
372
+
231
373
  {!isContextMenuDisabled && isContextMenuVisible && contextActions && (
232
374
  <RelativeListMenu
233
375
  options={contextActions}
@@ -242,10 +384,6 @@ export const ItemSlot: React.FC<IProps> = observer(
242
384
  }}
243
385
  />
244
386
  )}
245
-
246
- {isTooltipVisible && item && <ItemTooltip label={item.name} />}
247
-
248
- {onRenderSlot(item)}
249
387
  </Container>
250
388
  );
251
389
  }
@@ -261,12 +399,21 @@ const Container = styled.div`
261
399
  position: relative;
262
400
  `;
263
401
 
402
+ const ItemContainer = styled.div<{ isFocused?: boolean }>`
403
+ width: 100%;
404
+ height: 100%;
405
+ position: relative;
406
+
407
+ ${props => props.isFocused && 'z-index: 100; pointer-events: none;'}
408
+ `;
409
+
264
410
  const ItemQtyContainer = styled.div`
265
411
  position: relative;
266
412
  width: 85%;
267
413
  height: 16px;
268
414
  top: 25px;
269
415
  left: 2px;
416
+ pointer-events: none;
270
417
 
271
418
  display: flex;
272
419
  justify-content: flex-end;
@@ -1,7 +1,6 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { v4 as uuidv4 } from 'uuid';
4
- import { _RPGUI } from './RPGUIRoot';
5
4
 
6
5
  export enum RangeSliderType {
7
6
  Slider = 'rpgui-slider',
@@ -14,6 +13,7 @@ export interface IRangeSliderProps {
14
13
  valueMax: number;
15
14
  width: string;
16
15
  onChange: (value: number) => void;
16
+ value: number;
17
17
  }
18
18
 
19
19
  export const RangeSlider: React.FC<IRangeSliderProps> = ({
@@ -22,29 +22,46 @@ export const RangeSlider: React.FC<IRangeSliderProps> = ({
22
22
  valueMax,
23
23
  width,
24
24
  onChange,
25
+ value,
25
26
  }) => {
26
27
  const sliderId = uuidv4();
27
28
 
28
- const onHandleMouseUp = () => {
29
- const rpguiSlider = document.getElementById(`rpgui-slider-${sliderId}`);
30
- const value = _RPGUI.get_value(rpguiSlider);
29
+ const containerRef = useRef<HTMLDivElement>(null);
30
+ const [left, setLeft] = useState(0);
31
31
 
32
- onChange(Number(value));
33
- };
32
+ useEffect(() => {
33
+ const calculatedWidth = containerRef.current?.clientWidth || 0;
34
+ setLeft(
35
+ Math.max(
36
+ ((value - valueMin) / (valueMax - valueMin)) * (calculatedWidth - 35) +
37
+ 10
38
+ )
39
+ );
40
+ }, [value, valueMin, valueMax]);
41
+
42
+ const typeClass = type === RangeSliderType.GoldSlider ? 'golden' : '';
34
43
 
35
44
  return (
36
- <div onMouseUp={onHandleMouseUp}>
45
+ <div
46
+ style={{ width: width, position: 'relative' }}
47
+ className={`rpgui-slider-container ${typeClass}`}
48
+ id={`rpgui-slider-${sliderId}`}
49
+ ref={containerRef}
50
+ >
51
+ <div style={{ pointerEvents: 'none' }}>
52
+ <div className={`rpgui-slider-track ${typeClass}`} />
53
+ <div className={`rpgui-slider-left-edge ${typeClass}`} />
54
+ <div className={`rpgui-slider-right-edge ${typeClass}`} />
55
+ <div className={`rpgui-slider-thumb ${typeClass}`} style={{ left }} />
56
+ </div>
37
57
  <Input
38
- className={
39
- type === RangeSliderType.Slider
40
- ? RangeSliderType.Slider
41
- : RangeSliderType.GoldSlider
42
- }
43
58
  type="range"
44
59
  style={{ width: width }}
45
60
  min={valueMin}
46
61
  max={valueMax}
47
- id={`rpgui-slider-${sliderId}`}
62
+ onChange={e => onChange(Number(e.target.value))}
63
+ value={value}
64
+ className="rpgui-cursor-point"
48
65
  />
49
66
  </div>
50
67
  );
@@ -52,4 +69,10 @@ export const RangeSlider: React.FC<IRangeSliderProps> = ({
52
69
 
53
70
  const Input = styled.input`
54
71
  opacity: 0;
72
+ position: absolute;
73
+ width: 100%;
74
+ height: 100%;
75
+ top: 0;
76
+ left: 0;
77
+ margin-top: -5px;
55
78
  `;
package/src/index.tsx CHANGED
@@ -12,7 +12,6 @@ export * from './components/Input';
12
12
  export { ErrorBoundary } from './components/Item/Inventory/ErrorBoundary';
13
13
  export * from './components/Item/Inventory/ItemContainer';
14
14
  export * from './components/Item/Inventory/ItemSlot';
15
- export * from './components/itemSelector/ItemSelector';
16
15
  export * from './components/ListMenu';
17
16
  export * from './components/NPCDialog/NPCDialog';
18
17
  export * from './components/NPCDialog/NPCMultiDialog';
@@ -21,17 +20,17 @@ export * from './components/ProgressBar';
21
20
  export * from './components/PropertySelect/PropertySelect';
22
21
  export * from './components/QuestInfo/QuestInfo';
23
22
  export * from './components/QuestList';
24
- export * from './components/RadioButton';
25
- export * from './components/RangeSlider';
26
23
  export * from './components/RPGUIContainer';
27
- export * from './components/RPGUIForceRenderStart';
28
24
  export * from './components/RPGUIRoot';
29
- export * from './components/shared/SpriteFromAtlas';
25
+ export * from './components/RadioButton';
26
+ export * from './components/RangeSlider';
30
27
  export * from './components/SkillProgressBar';
31
28
  export * from './components/SkillsContainer';
32
29
  export * from './components/TextArea';
33
30
  export * from './components/TimeWidget/TimeWidget';
34
31
  export * from './components/TradingMenu/TradingMenu';
35
32
  export * from './components/Truncate';
33
+ export * from './components/itemSelector/ItemSelector';
34
+ export * from './components/shared/SpriteFromAtlas';
36
35
  export * from './components/typography/DynamicText';
37
36
  export { useEventListener } from './hooks/useEventListener';
@@ -242,7 +242,7 @@ export const items: IItem[] = [
242
242
  updatedAt: '2022-06-04T18:16:49.056Z',
243
243
  },
244
244
  {
245
- _id: '392acek4j7c8e80d2ff60404',
245
+ _id: '392acek4j7c8e80d2fs60404',
246
246
  hasUseWith: false,
247
247
  type: ItemType.Accessory,
248
248
  subType: ItemSubType.Ranged,
@@ -9,6 +9,7 @@ import { RPGUIRoot } from '../../src/components/RPGUIRoot';
9
9
  import { equipmentSetMock } from '../../src/mocks/equipmentSet.mocks';
10
10
  import atlasJSON from '../mocks/atlas/items/items.json';
11
11
  import atlasIMG from '../mocks/atlas/items/items.png';
12
+ import { IPosition } from '../types/eventTypes';
12
13
 
13
14
  const meta: Meta = {
14
15
  title: 'Equipment Set',
@@ -36,6 +37,10 @@ const onSelected = (payload: string) => {
36
37
  console.log('dispatch', payload);
37
38
  };
38
39
 
40
+ const onItemOutsideDrop = (item: IItem, position: IPosition) => {
41
+ console.log('dropped outside', item, position);
42
+ };
43
+
39
44
  const Template: Story<IEquipmentSetProps> = args => (
40
45
  <RPGUIRoot>
41
46
  <EquipmentSet
@@ -45,9 +50,14 @@ const Template: Story<IEquipmentSetProps> = args => (
45
50
  onMouseOver={onMouseOver}
46
51
  onSelected={onSelected}
47
52
  onItemClick={onItemClick}
53
+ onItemDragEnd={_quantity => {}}
54
+ onItemDragStart={item => console.log('drag start: ', item._id)}
55
+ onItemPlaceDrop={item => console.log('dropped to: ', item?._id)}
56
+ checkIfItemCanBeMoved={() => false}
48
57
  type={ItemContainerType.Equipment}
49
58
  atlasIMG={atlasIMG}
50
59
  atlasJSON={atlasJSON}
60
+ onItemOutsideDrop={onItemOutsideDrop}
51
61
  />
52
62
  </RPGUIRoot>
53
63
  );
@@ -1,6 +1,6 @@
1
1
  import { IItem, ItemContainerType, ItemType } from '@rpg-engine/shared';
2
2
  import { Meta, Story } from '@storybook/react';
3
- import React from 'react';
3
+ import React, { useState } from 'react';
4
4
  import {
5
5
  IItemContainerProps,
6
6
  ItemContainer,
@@ -37,19 +37,87 @@ const onItemClick = (
37
37
  console.log(item, ItemType, itemContainerType, 'was clicked!');
38
38
  };
39
39
 
40
- const Template: Story<IItemContainerProps> = () => (
41
- <RPGUIRoot>
42
- <ItemContainer
43
- itemContainer={itemContainerMock}
44
- onClose={() => console.log('closing item container')}
45
- onMouseOver={onMouseOver}
46
- onSelected={onSelected}
47
- onItemClick={onItemClick}
48
- type={ItemContainerType.Inventory}
49
- atlasIMG={atlasIMG}
50
- atlasJSON={atlasJSON}
51
- />
52
- </RPGUIRoot>
53
- );
40
+ // THIS IS ONLY LONG-BOW EXAMPLE FOR DRAG AND DROP
41
+ let dragItem: IItem | null = null;
42
+ let dropItem: IItem | null = null;
43
+ let allowedToDrop = false;
44
+ let dragSlot = -1;
45
+ let dropSlot = -1;
46
+
47
+ const Template: Story<IItemContainerProps> = () => {
48
+ const [itemContainer, setItemContainer] = useState(itemContainerMock);
49
+
50
+ return (
51
+ <RPGUIRoot>
52
+ <ItemContainer
53
+ itemContainer={itemContainer}
54
+ onClose={() => console.log('closing item container')}
55
+ onMouseOver={onMouseOver}
56
+ onSelected={onSelected}
57
+ onItemClick={onItemClick}
58
+ checkIfItemCanBeMoved={() => allowedToDrop}
59
+ onItemDragEnd={quantity => {
60
+ // THIS IS ONLY LONG-BOW EXAMPLE FOR DRAG AND DROP
61
+
62
+ if (quantity === 0) {
63
+ setItemContainer({ ...itemContainer });
64
+ } else if (allowedToDrop && dropSlot !== -1) {
65
+ const newContainer = { ...itemContainer };
66
+
67
+ if (quantity && dragItem && dragItem.stackQty) {
68
+ newContainer.slots[dropSlot] = {
69
+ ...dragItem,
70
+ stackQty: quantity + (dropItem?.stackQty || 0),
71
+ };
72
+
73
+ if (dragItem.stackQty - quantity === 0)
74
+ newContainer.slots[dragSlot] = null;
75
+ else
76
+ newContainer.slots[dragSlot] = {
77
+ ...dragItem,
78
+ stackQty: dragItem.stackQty - quantity,
79
+ };
80
+ } else {
81
+ newContainer.slots[dropSlot] = dragItem;
82
+ newContainer.slots[dragSlot] = null;
83
+ }
84
+
85
+ setItemContainer(newContainer);
86
+ }
87
+
88
+ allowedToDrop = false;
89
+ dropSlot = -1;
90
+ dropItem = null;
91
+ dragItem = null;
92
+ }}
93
+ onItemDragStart={(item, slotIndex) => {
94
+ dragItem = item;
95
+ dragSlot = slotIndex;
96
+ }}
97
+ onItemPlaceDrop={(item, slotIndex) => {
98
+ // THIS IS ONLY LONG-BOW EXAMPLE FOR DRAG AND DROP
99
+
100
+ if (
101
+ !item ||
102
+ (dragItem?.key === item?.key && dragSlot !== slotIndex)
103
+ ) {
104
+ console.log('allow');
105
+ allowedToDrop = true;
106
+ dropSlot = slotIndex;
107
+ dropItem = item ? item : null;
108
+ } else {
109
+ allowedToDrop = false;
110
+ dropSlot = -1;
111
+ dropItem = null;
112
+ }
113
+ }}
114
+ onOutsideDrop={(item, pos) => console.log('drop', item, pos)}
115
+ type={ItemContainerType.Inventory}
116
+ atlasIMG={atlasIMG}
117
+ atlasJSON={atlasJSON}
118
+ />
119
+ </RPGUIRoot>
120
+ );
121
+ };
54
122
 
55
123
  export const Default = Template.bind({});