@rpg-engine/long-bow 0.6.7 → 0.6.9
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/Stepper.d.ts +4 -0
- package/dist/components/Tutorial/TutorialStepper.d.ts +12 -0
- package/dist/index.d.ts +6 -5
- package/dist/long-bow.cjs.development.js +757 -311
- 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 +758 -313
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/stories/TutorialStepper.stories.d.ts +5 -0
- package/package.json +2 -2
- package/src/assets/images/sewer.png +0 -0
- package/src/assets/images/shortcuts.png +0 -0
- package/src/components/Stepper.tsx +103 -28
- package/src/components/Tutorial/TutorialStepper.tsx +72 -0
- package/src/index.tsx +7 -5
- package/src/stories/TutorialStepper.stories.tsx +48 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Meta } from '@storybook/react';
|
|
2
|
+
import { ITutorialStepperProps } from '../components/Tutorial/TutorialStepper';
|
|
3
|
+
declare const meta: Meta;
|
|
4
|
+
export default meta;
|
|
5
|
+
export declare const Default: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react").ReactFramework, ITutorialStepperProps>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpg-engine/long-bow",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": "
|
|
19
|
+
"node": "^16.0.0"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"dev": "yarn install && start-storybook -p 6006",
|
|
Binary file
|
|
Binary file
|
|
@@ -7,6 +7,8 @@ import { Button, ButtonTypes } from './Button';
|
|
|
7
7
|
interface IStep {
|
|
8
8
|
component: React.ReactNode;
|
|
9
9
|
id: number;
|
|
10
|
+
validate?: () => boolean | Promise<boolean>;
|
|
11
|
+
errorMessage?: string;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
interface IStepperProps {
|
|
@@ -15,19 +17,83 @@ interface IStepperProps {
|
|
|
15
17
|
label: string;
|
|
16
18
|
onClick: (() => void) | (() => Promise<void>) | ((e: any) => Promise<void>);
|
|
17
19
|
};
|
|
20
|
+
onError?: (message: string) => void;
|
|
21
|
+
useSideArrows?: boolean; // New prop to control arrow position
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
export const Stepper: React.FC<IStepperProps> = ({
|
|
24
|
+
export const Stepper: React.FC<IStepperProps> = ({
|
|
25
|
+
steps,
|
|
26
|
+
finalCTAButton,
|
|
27
|
+
onError,
|
|
28
|
+
useSideArrows = false, // Default to false for backwards compatibility
|
|
29
|
+
}) => {
|
|
21
30
|
const [currentStep, setCurrentStep] = useState(0);
|
|
22
31
|
|
|
23
32
|
const currentComponent = steps[currentStep]?.component;
|
|
24
33
|
|
|
25
34
|
const totalSteps = steps.length;
|
|
26
35
|
|
|
27
|
-
const onStepChange = (step: number) => {
|
|
36
|
+
const onStepChange = async (step: number) => {
|
|
37
|
+
if (steps[currentStep]?.validate) {
|
|
38
|
+
try {
|
|
39
|
+
const isValid = await steps[currentStep].validate!();
|
|
40
|
+
if (!isValid) {
|
|
41
|
+
if (onError) {
|
|
42
|
+
onError(
|
|
43
|
+
steps[currentStep].errorMessage ||
|
|
44
|
+
`Validation failed on step ${currentStep + 1}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (onError) {
|
|
51
|
+
onError(
|
|
52
|
+
`An error occurred during validation on step ${currentStep + 1}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
28
58
|
setCurrentStep(step);
|
|
29
59
|
};
|
|
30
60
|
|
|
61
|
+
const renderArrows = () => {
|
|
62
|
+
const leftArrow = (
|
|
63
|
+
<SelectArrow
|
|
64
|
+
direction="left"
|
|
65
|
+
onPointerDown={() => onStepChange(Math.max(0, currentStep - 1))}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const rightArrow = (
|
|
70
|
+
<SelectArrow
|
|
71
|
+
direction="right"
|
|
72
|
+
onPointerDown={() =>
|
|
73
|
+
onStepChange(Math.min(totalSteps - 1, currentStep + 1))
|
|
74
|
+
}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (useSideArrows) {
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
{currentStep > 0 && <ArrowContainer left>{leftArrow}</ArrowContainer>}
|
|
82
|
+
{currentStep < totalSteps - 1 && (
|
|
83
|
+
<ArrowContainer right>{rightArrow}</ArrowContainer>
|
|
84
|
+
)}
|
|
85
|
+
</>
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
{currentStep > 0 && leftArrow}
|
|
91
|
+
{currentStep < totalSteps - 1 && rightArrow}
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
31
97
|
return (
|
|
32
98
|
<StepperContainer className="stepper-container">
|
|
33
99
|
<StepperTop>
|
|
@@ -40,29 +106,17 @@ export const Stepper: React.FC<IStepperProps> = ({ steps, finalCTAButton }) => {
|
|
|
40
106
|
))}
|
|
41
107
|
</StepperTop>
|
|
42
108
|
|
|
43
|
-
<
|
|
109
|
+
<StepperBodyContainer>
|
|
110
|
+
{useSideArrows && renderArrows()}
|
|
111
|
+
<StepperBody>{currentComponent}</StepperBody>
|
|
112
|
+
</StepperBodyContainer>
|
|
44
113
|
|
|
45
114
|
<StepperFooter>
|
|
46
|
-
{
|
|
47
|
-
<SelectArrow
|
|
48
|
-
direction="left"
|
|
49
|
-
onPointerDown={() => onStepChange(Math.max(0, currentStep - 1))}
|
|
50
|
-
/>
|
|
51
|
-
)}
|
|
52
|
-
|
|
53
|
-
{currentStep < totalSteps - 1 && (
|
|
54
|
-
<SelectArrow
|
|
55
|
-
direction="right"
|
|
56
|
-
onPointerDown={() =>
|
|
57
|
-
onStepChange(Math.min(totalSteps - 1, currentStep + 1))
|
|
58
|
-
}
|
|
59
|
-
/>
|
|
60
|
-
)}
|
|
61
|
-
|
|
115
|
+
{!useSideArrows && renderArrows()}
|
|
62
116
|
{currentStep === totalSteps - 1 && finalCTAButton && (
|
|
63
117
|
<Button
|
|
64
118
|
buttonType={ButtonTypes.RPGUIButton}
|
|
65
|
-
|
|
119
|
+
onClick={finalCTAButton.onClick}
|
|
66
120
|
>
|
|
67
121
|
{finalCTAButton.label}
|
|
68
122
|
</Button>
|
|
@@ -75,36 +129,48 @@ export const Stepper: React.FC<IStepperProps> = ({ steps, finalCTAButton }) => {
|
|
|
75
129
|
const StepperContainer = styled.div`
|
|
76
130
|
display: flex;
|
|
77
131
|
flex-direction: column;
|
|
78
|
-
height: 100%;
|
|
132
|
+
height: 100%;
|
|
79
133
|
`;
|
|
80
134
|
|
|
81
135
|
const StepperTop = styled.div`
|
|
82
|
-
flex: 1;
|
|
83
|
-
|
|
136
|
+
flex: 1;
|
|
84
137
|
display: flex;
|
|
85
138
|
flex-wrap: wrap;
|
|
86
139
|
justify-content: center;
|
|
87
140
|
align-items: center;
|
|
88
|
-
|
|
89
141
|
margin-bottom: 1rem;
|
|
90
142
|
`;
|
|
91
143
|
|
|
144
|
+
const StepperBodyContainer = styled.div`
|
|
145
|
+
flex: 8;
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
position: relative;
|
|
149
|
+
`;
|
|
150
|
+
|
|
92
151
|
const StepperBody = styled.div`
|
|
93
|
-
flex:
|
|
152
|
+
flex: 1;
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
const ArrowContainer = styled.div<{ left?: boolean; right?: boolean }>`
|
|
156
|
+
position: absolute;
|
|
157
|
+
top: 50%;
|
|
158
|
+
transform: translateY(-50%);
|
|
159
|
+
${props => props.left && 'left: -30px;'}
|
|
160
|
+
${props => props.right && 'right: -30px;'}
|
|
94
161
|
`;
|
|
95
162
|
|
|
96
163
|
const StepperFooter = styled.div`
|
|
97
164
|
margin-top: 1rem;
|
|
98
|
-
flex: 1;
|
|
99
|
-
|
|
165
|
+
flex: 1;
|
|
100
166
|
display: flex;
|
|
101
167
|
justify-content: flex-end;
|
|
168
|
+
align-items: center;
|
|
102
169
|
`;
|
|
103
170
|
|
|
104
171
|
const ProgressIndicator = styled.div<{ isActive: boolean }>`
|
|
105
172
|
width: 20px;
|
|
106
173
|
height: 20px;
|
|
107
|
-
border-radius: 50%;
|
|
108
174
|
background-color: ${({ isActive }) =>
|
|
109
175
|
isActive ? uiColors.orange : uiColors.lightGray};
|
|
110
176
|
margin: 0 5px;
|
|
@@ -112,4 +178,13 @@ const ProgressIndicator = styled.div<{ isActive: boolean }>`
|
|
|
112
178
|
opacity: ${({ isActive }) => (isActive ? 1 : 0.25)};
|
|
113
179
|
border: 1px solid ${uiColors.raisinBlack};
|
|
114
180
|
cursor: pointer;
|
|
181
|
+
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
|
|
182
|
+
/* Create a diamond shape */
|
|
183
|
+
box-shadow: ${({ isActive }) =>
|
|
184
|
+
isActive
|
|
185
|
+
? '0 0 0 1px black, 1px 1px 0 1px black, 2px 2px 0 1px black, -1px -1px 0 1px black'
|
|
186
|
+
: '0 0 0 1px black, 1px 1px 0 1px gray, 2px 2px 0 1px gray, -1px -1px 0 1px gray'};
|
|
187
|
+
/* Add pixel art border effect */
|
|
188
|
+
border-radius: 0;
|
|
189
|
+
/* Remove border-radius to make it square */
|
|
115
190
|
`;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Stepper } from '../Stepper';
|
|
4
|
+
import { DynamicText } from '../typography/DynamicText';
|
|
5
|
+
|
|
6
|
+
export interface ITutorialLesson {
|
|
7
|
+
title: string;
|
|
8
|
+
body?: React.ReactNode | string;
|
|
9
|
+
text?: string;
|
|
10
|
+
image: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ITutorialStepperProps {
|
|
14
|
+
lessons: ITutorialLesson[];
|
|
15
|
+
onLessonFinish: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const TutorialStepper = ({
|
|
19
|
+
lessons,
|
|
20
|
+
onLessonFinish,
|
|
21
|
+
}: ITutorialStepperProps) => {
|
|
22
|
+
const generateLessons = () => {
|
|
23
|
+
return lessons.map((lesson, index) => ({
|
|
24
|
+
component: (
|
|
25
|
+
<div>
|
|
26
|
+
<h1>{lesson.title}</h1>
|
|
27
|
+
{lesson.image && (
|
|
28
|
+
<LessonImage>
|
|
29
|
+
<img src={lesson.image} alt={lesson.title} />
|
|
30
|
+
</LessonImage>
|
|
31
|
+
)}
|
|
32
|
+
{lesson.body && <LessonBody>{lesson.body}</LessonBody>}
|
|
33
|
+
{lesson.text && <DynamicText text={lesson.text} />}
|
|
34
|
+
</div>
|
|
35
|
+
),
|
|
36
|
+
id: index,
|
|
37
|
+
}));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Container>
|
|
42
|
+
<Stepper
|
|
43
|
+
steps={generateLessons()}
|
|
44
|
+
finalCTAButton={{
|
|
45
|
+
label: 'Close',
|
|
46
|
+
onClick: () => {
|
|
47
|
+
onLessonFinish();
|
|
48
|
+
},
|
|
49
|
+
}}
|
|
50
|
+
useSideArrows
|
|
51
|
+
/>
|
|
52
|
+
</Container>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const LessonBody = styled.div``;
|
|
57
|
+
|
|
58
|
+
const Container = styled.div`
|
|
59
|
+
margin: 2rem;
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const LessonImage = styled.div`
|
|
63
|
+
display: flex;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
align-items: center;
|
|
66
|
+
|
|
67
|
+
img {
|
|
68
|
+
width: 600px;
|
|
69
|
+
height: 400px;
|
|
70
|
+
border-radius: 10px;
|
|
71
|
+
}
|
|
72
|
+
`;
|
package/src/index.tsx
CHANGED
|
@@ -2,8 +2,8 @@ export * from './components/AsyncDropdown';
|
|
|
2
2
|
export * from './components/Button';
|
|
3
3
|
export * from './components/Character/CharacterSelection';
|
|
4
4
|
export * from './components/Chat/Chat';
|
|
5
|
-
export * from './components/ChatRevamp/ChatRevamp';
|
|
6
5
|
export * from './components/Chatdeprecated/ChatDeprecated';
|
|
6
|
+
export * from './components/ChatRevamp/ChatRevamp';
|
|
7
7
|
export * from './components/CheckButton';
|
|
8
8
|
export * from './components/CheckItem';
|
|
9
9
|
export * from './components/CircularController/CircularController';
|
|
@@ -20,6 +20,7 @@ export * from './components/Input';
|
|
|
20
20
|
export { ErrorBoundary } from './components/Item/Inventory/ErrorBoundary';
|
|
21
21
|
export * from './components/Item/Inventory/ItemContainer';
|
|
22
22
|
export * from './components/Item/Inventory/ItemSlot';
|
|
23
|
+
export * from './components/itemSelector/ItemSelector';
|
|
23
24
|
export * from './components/Leaderboard/Leaderboard';
|
|
24
25
|
export * from './components/ListMenu';
|
|
25
26
|
export * from './components/Marketplace/Marketplace';
|
|
@@ -34,10 +35,11 @@ export * from './components/ProgressBar';
|
|
|
34
35
|
export * from './components/PropertySelect/PropertySelect';
|
|
35
36
|
export * from './components/QuestInfo/QuestInfo';
|
|
36
37
|
export * from './components/QuestList';
|
|
37
|
-
export * from './components/RPGUI/RPGUIContainer';
|
|
38
|
-
export * from './components/RPGUI/RPGUIRoot';
|
|
39
38
|
export * from './components/RadioButton';
|
|
40
39
|
export * from './components/RangeSlider';
|
|
40
|
+
export * from './components/RPGUI/RPGUIContainer';
|
|
41
|
+
export * from './components/RPGUI/RPGUIRoot';
|
|
42
|
+
export * from './components/shared/SpriteFromAtlas';
|
|
41
43
|
export * from './components/Shortcuts/Shortcuts';
|
|
42
44
|
export * from './components/SkillProgressBar';
|
|
43
45
|
export * from './components/SkillsContainer';
|
|
@@ -47,7 +49,7 @@ export * from './components/TextArea';
|
|
|
47
49
|
export * from './components/TimeWidget/TimeWidget';
|
|
48
50
|
export * from './components/TradingMenu/TradingMenu';
|
|
49
51
|
export * from './components/Truncate';
|
|
50
|
-
export * from './components/
|
|
51
|
-
export * from './components/shared/SpriteFromAtlas';
|
|
52
|
+
export * from './components/Tutorial/TutorialStepper';
|
|
52
53
|
export * from './components/typography/DynamicText';
|
|
54
|
+
|
|
53
55
|
export { useEventListener } from './hooks/useEventListener';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Meta, Story } from '@storybook/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { RPGUIRoot } from '..';
|
|
4
|
+
import {
|
|
5
|
+
ITutorialLesson,
|
|
6
|
+
ITutorialStepperProps,
|
|
7
|
+
TutorialStepper,
|
|
8
|
+
} from '../components/Tutorial/TutorialStepper';
|
|
9
|
+
|
|
10
|
+
import tutorialImg1 from '../assets/images/sewer.png';
|
|
11
|
+
import tutorialImg2 from '../assets/images/shortcuts.png';
|
|
12
|
+
|
|
13
|
+
const meta: Meta = {
|
|
14
|
+
title: 'Tutorial/TutorialStepper',
|
|
15
|
+
component: TutorialStepper,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default meta;
|
|
19
|
+
|
|
20
|
+
const Template: Story<ITutorialStepperProps> = () => {
|
|
21
|
+
const lessons: ITutorialLesson[] = [
|
|
22
|
+
{
|
|
23
|
+
title: 'Sample Lesson 1',
|
|
24
|
+
text: 'This is the first lesson',
|
|
25
|
+
image: tutorialImg1,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: 'Sample Lesson 2',
|
|
29
|
+
text: 'This is the second lesson',
|
|
30
|
+
image: tutorialImg2,
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<RPGUIRoot>
|
|
36
|
+
<TutorialStepper
|
|
37
|
+
lessons={lessons}
|
|
38
|
+
onLessonFinish={() => {
|
|
39
|
+
console.log('finished lesson!');
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
</RPGUIRoot>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const Default = Template.bind({});
|
|
47
|
+
|
|
48
|
+
Default.args = {};
|