@rpg-engine/long-bow 0.6.8 → 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 +1 -0
- package/dist/components/Tutorial/TutorialStepper.d.ts +12 -0
- package/dist/index.d.ts +6 -5
- package/dist/long-bow.cjs.development.js +398 -329
- 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 +399 -331
- 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 +72 -27
- 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
|
|
@@ -18,12 +18,14 @@ interface IStepperProps {
|
|
|
18
18
|
onClick: (() => void) | (() => Promise<void>) | ((e: any) => Promise<void>);
|
|
19
19
|
};
|
|
20
20
|
onError?: (message: string) => void;
|
|
21
|
+
useSideArrows?: boolean; // New prop to control arrow position
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const Stepper: React.FC<IStepperProps> = ({
|
|
24
25
|
steps,
|
|
25
26
|
finalCTAButton,
|
|
26
27
|
onError,
|
|
28
|
+
useSideArrows = false, // Default to false for backwards compatibility
|
|
27
29
|
}) => {
|
|
28
30
|
const [currentStep, setCurrentStep] = useState(0);
|
|
29
31
|
|
|
@@ -36,7 +38,6 @@ export const Stepper: React.FC<IStepperProps> = ({
|
|
|
36
38
|
try {
|
|
37
39
|
const isValid = await steps[currentStep].validate!();
|
|
38
40
|
if (!isValid) {
|
|
39
|
-
// If the current step is not valid, prevent navigation and trigger onError
|
|
40
41
|
if (onError) {
|
|
41
42
|
onError(
|
|
42
43
|
steps[currentStep].errorMessage ||
|
|
@@ -46,7 +47,6 @@ export const Stepper: React.FC<IStepperProps> = ({
|
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
} catch (error) {
|
|
49
|
-
// Handle unexpected validation errors
|
|
50
50
|
if (onError) {
|
|
51
51
|
onError(
|
|
52
52
|
`An error occurred during validation on step ${currentStep + 1}`
|
|
@@ -58,6 +58,42 @@ export const Stepper: React.FC<IStepperProps> = ({
|
|
|
58
58
|
setCurrentStep(step);
|
|
59
59
|
};
|
|
60
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
|
+
|
|
61
97
|
return (
|
|
62
98
|
<StepperContainer className="stepper-container">
|
|
63
99
|
<StepperTop>
|
|
@@ -70,25 +106,13 @@ export const Stepper: React.FC<IStepperProps> = ({
|
|
|
70
106
|
))}
|
|
71
107
|
</StepperTop>
|
|
72
108
|
|
|
73
|
-
<
|
|
109
|
+
<StepperBodyContainer>
|
|
110
|
+
{useSideArrows && renderArrows()}
|
|
111
|
+
<StepperBody>{currentComponent}</StepperBody>
|
|
112
|
+
</StepperBodyContainer>
|
|
74
113
|
|
|
75
114
|
<StepperFooter>
|
|
76
|
-
{
|
|
77
|
-
<SelectArrow
|
|
78
|
-
direction="left"
|
|
79
|
-
onPointerDown={() => onStepChange(Math.max(0, currentStep - 1))}
|
|
80
|
-
/>
|
|
81
|
-
)}
|
|
82
|
-
|
|
83
|
-
{currentStep < totalSteps - 1 && (
|
|
84
|
-
<SelectArrow
|
|
85
|
-
direction="right"
|
|
86
|
-
onPointerDown={() =>
|
|
87
|
-
onStepChange(Math.min(totalSteps - 1, currentStep + 1))
|
|
88
|
-
}
|
|
89
|
-
/>
|
|
90
|
-
)}
|
|
91
|
-
|
|
115
|
+
{!useSideArrows && renderArrows()}
|
|
92
116
|
{currentStep === totalSteps - 1 && finalCTAButton && (
|
|
93
117
|
<Button
|
|
94
118
|
buttonType={ButtonTypes.RPGUIButton}
|
|
@@ -105,36 +129,48 @@ export const Stepper: React.FC<IStepperProps> = ({
|
|
|
105
129
|
const StepperContainer = styled.div`
|
|
106
130
|
display: flex;
|
|
107
131
|
flex-direction: column;
|
|
108
|
-
height: 100%;
|
|
132
|
+
height: 100%;
|
|
109
133
|
`;
|
|
110
134
|
|
|
111
135
|
const StepperTop = styled.div`
|
|
112
|
-
flex: 1;
|
|
113
|
-
|
|
136
|
+
flex: 1;
|
|
114
137
|
display: flex;
|
|
115
138
|
flex-wrap: wrap;
|
|
116
139
|
justify-content: center;
|
|
117
140
|
align-items: center;
|
|
118
|
-
|
|
119
141
|
margin-bottom: 1rem;
|
|
120
142
|
`;
|
|
121
143
|
|
|
144
|
+
const StepperBodyContainer = styled.div`
|
|
145
|
+
flex: 8;
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
position: relative;
|
|
149
|
+
`;
|
|
150
|
+
|
|
122
151
|
const StepperBody = styled.div`
|
|
123
|
-
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;'}
|
|
124
161
|
`;
|
|
125
162
|
|
|
126
163
|
const StepperFooter = styled.div`
|
|
127
164
|
margin-top: 1rem;
|
|
128
|
-
flex: 1;
|
|
129
|
-
|
|
165
|
+
flex: 1;
|
|
130
166
|
display: flex;
|
|
131
167
|
justify-content: flex-end;
|
|
168
|
+
align-items: center;
|
|
132
169
|
`;
|
|
133
170
|
|
|
134
171
|
const ProgressIndicator = styled.div<{ isActive: boolean }>`
|
|
135
172
|
width: 20px;
|
|
136
173
|
height: 20px;
|
|
137
|
-
border-radius: 50%;
|
|
138
174
|
background-color: ${({ isActive }) =>
|
|
139
175
|
isActive ? uiColors.orange : uiColors.lightGray};
|
|
140
176
|
margin: 0 5px;
|
|
@@ -142,4 +178,13 @@ const ProgressIndicator = styled.div<{ isActive: boolean }>`
|
|
|
142
178
|
opacity: ${({ isActive }) => (isActive ? 1 : 0.25)};
|
|
143
179
|
border: 1px solid ${uiColors.raisinBlack};
|
|
144
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 */
|
|
145
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 = {};
|