Skip to content

Commit 0c28916

Browse files
committed
feat: tutorial-overlay basic
0 parents  commit 0c28916

16 files changed

Lines changed: 9199 additions & 0 deletions

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
*.log
2+
.DS_Store
3+
node_modules
4+
.cache
5+
coverage
6+
dist
7+
/headless
8+
.vscode
9+
.vercel

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 sjsjsj1246
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<div align="center"><a name="readme-top"></a>
2+
3+
<h1>React Tutorial Overlay</h1>
4+
5+
A headless library that makes it easy to put tutorials on top of the screen.
6+
7+
(This is an open source library that is still under development.)
8+
9+
</div>
10+
11+
## Features
12+
13+
- ✨ step-by-step tutorial overlay
14+
- 🎨 easily customizable
15+
- 🤝 Pomise API (Coming Soon)
16+
- 🚀 Lightweight (Coming Soon)
17+
- 👻 Headless (Coming Soon)
18+
19+
## Get Started
20+
21+
**install with npm**
22+
23+
```bash
24+
npm install react-tutorial-overlay
25+
```
26+
27+
**install with yarn**
28+
29+
```bash
30+
yarn add react-tutorial-overlay
31+
```
32+
33+
```jsx
34+
import { TutorialOverlay, tutorial } from '../src';
35+
36+
const App = () => {
37+
const handleClick = () => {
38+
tutorial.open([
39+
{
40+
targetIds: ['target1'],
41+
title: 'title',
42+
content: 'content',
43+
},
44+
]);
45+
};
46+
47+
return (
48+
<div>
49+
<button onClick={handleClick}>open</button>
50+
<TutorialOverlay />
51+
</div>
52+
);
53+
};
54+
```
55+
56+
## Documentation
57+
58+
- [Document](./document/index.md)
59+
60+
## Contributing
61+
62+
@sjsjsj1246

package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "react-tutorial-overlay",
3+
"description": "A headless library that makes it easy to put tutorials on top of the screen.",
4+
"version": "0.1.0",
5+
"author": "sjsjsj1246",
6+
"repository": "sjsjsj1246/react-tutorial-overlay",
7+
"license": "MIT",
8+
"scripts": {
9+
"main": "pnpm -F @react-tutorial-overlay/main",
10+
"document": "pnpm -F @react-tutorial-overlay/document",
11+
"dev": "pnpm -r dev",
12+
"build": "pnpm main build",
13+
"size": "size-limit"
14+
},
15+
"keywords": [
16+
"react",
17+
"tutorial"
18+
],
19+
"main": "dist/index.js",
20+
"types": "dist/index.d.ts",
21+
"files": [
22+
"dist"
23+
],
24+
"size-limit": [
25+
{
26+
"path": "dist/index.js",
27+
"limit": "10 KB"
28+
},
29+
{
30+
"path": "dist/index.mjs",
31+
"limit": "10 KB"
32+
}
33+
],
34+
"dependencies": {
35+
"goober": "^2.1.10",
36+
"react": "^18.2.0",
37+
"react-dom": "^18.2.0"
38+
},
39+
"devDependencies": {
40+
"@size-limit/preset-small-lib": "^7.0.8",
41+
"size-limit": "^7.0.8"
42+
}
43+
}

packages/main/jest.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'jsdom',
5+
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
6+
};

packages/main/package.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@react-tutorial-overlay/main",
3+
"scripts": {
4+
"dev": "tsup --watch",
5+
"build": "tsup",
6+
"test": "jest --runInBand"
7+
},
8+
"husky": {
9+
"hooks": {
10+
"pre-commit": "prettier src --ignore-unknown --write"
11+
}
12+
},
13+
"prettier": {
14+
"singleQuote": true,
15+
"semi": true,
16+
"useTabs": false,
17+
"printWidth": 120
18+
},
19+
"devDependencies": {
20+
"@jest/types": "^28.1.3",
21+
"@testing-library/jest-dom": "^5.16.4",
22+
"@testing-library/react": "^13.3.0",
23+
"@types/dompurify": "^3.0.5",
24+
"@types/jest": "^28.1.5",
25+
"@types/react": "^18.0.15",
26+
"@types/react-dom": "^18.0.6",
27+
"@types/testing-library__jest-dom": "^5.14.5",
28+
"csstype": "^3.1.0",
29+
"jest": "^28.1.3",
30+
"jest-environment-jsdom": "^28.1.3",
31+
"prettier": "^3.2.5",
32+
"react": "^18.2.0",
33+
"react-dom": "^18.2.0",
34+
"ts-jest": "^28.0.6",
35+
"tslib": "^2.4.0",
36+
"tsup": "^6.7.0",
37+
"typescript": "^5.0.4"
38+
},
39+
"dependencies": {
40+
"goober": "^2.1.10"
41+
},
42+
"peerDependencies": {
43+
"react": ">=16",
44+
"react-dom": ">=16"
45+
}
46+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import React from 'react';
2+
import { ActionType, dispatch, useTutorialStore } from '../core/store';
3+
import { styled } from 'goober';
4+
5+
export const Content = React.forwardRef((_, ref?: React.ForwardedRef<HTMLInputElement>) => {
6+
const {
7+
index,
8+
tutorial: { steps },
9+
} = useTutorialStore();
10+
const currentStep = steps[index];
11+
const { onPrevStep, onNextStep } = currentStep;
12+
13+
const handlePrev = () => {
14+
onPrevStep?.();
15+
dispatch({ type: ActionType.PREV });
16+
};
17+
18+
const handleNext = () => {
19+
onNextStep?.();
20+
dispatch({ type: ActionType.NEXT });
21+
};
22+
23+
const handleClose = () => {
24+
dispatch({ type: ActionType.CLOSE });
25+
};
26+
27+
return (
28+
<Wrapper ref={ref}>
29+
<Heander>
30+
<InfoTitle>
31+
<p>{currentStep.title}</p>
32+
<button onClick={handleClose}>건너뛰기</button>
33+
</InfoTitle>
34+
<InfoContent dangerouslySetInnerHTML={{ __html: currentStep.content ?? '' }} />
35+
</Heander>
36+
<Footer className="flex items-center justify-between">
37+
<InfoSteps className="text-[.75rem] font-medium text-sub-2.5">
38+
<span>{`${index + 1} / ${steps.length}`}</span>
39+
</InfoSteps>
40+
<ButtonWrapper className="flex gap-[.625rem]">
41+
{index !== 0 && <button onClick={handlePrev}>이전</button>}
42+
<button onClick={handleNext}>{index === steps.length - 1 ? '완료' : '다음'}</button>
43+
</ButtonWrapper>
44+
</Footer>
45+
</Wrapper>
46+
);
47+
});
48+
49+
const Wrapper = styled('div', React.forwardRef)`
50+
position: absolute;
51+
top: 6.25rem;
52+
z-index: 999;
53+
width: 20rem;
54+
min-height: 7.5rem;
55+
display: flex;
56+
flex-direction: column;
57+
border-radius: 0.625rem;
58+
background-color: white;
59+
padding: 1rem;
60+
box-shadow: 5px 5px 15px 0px rgba(142, 142, 142, 0.3);
61+
`;
62+
63+
const Heander = styled('div')`
64+
display: flex;
65+
flex-direction: column;
66+
flex: 1;
67+
`;
68+
69+
const InfoTitle = styled('div')`
70+
display: flex;
71+
align-items: center;
72+
73+
p {
74+
font-size: 1.25rem;
75+
font-weight: 600;
76+
color: #1f1f1f;
77+
}
78+
79+
button {
80+
font-size: 0.75rem;
81+
font-weight: 500;
82+
color: #1f1f1f;
83+
margin-left: auto;
84+
text-decoration: underline;
85+
text-underline-offset: 0.1rem;
86+
}
87+
`;
88+
89+
const InfoContent = styled('div')`
90+
margin-top: 1rem;
91+
flex: 1;
92+
overflow-y: scroll;
93+
`;
94+
95+
const Footer = styled('div')`
96+
display: flex;
97+
align-items: center;
98+
justify-content: space-between;
99+
`;
100+
101+
const InfoSteps = styled('div')`
102+
display: flex;
103+
font-size: 0.75rem;
104+
font-weight: 500;
105+
color: #1f1f1f;
106+
`;
107+
108+
const ButtonWrapper = styled('div')`
109+
display: flex;
110+
gap: 0.625rem;
111+
112+
button {
113+
display: flex;
114+
align-items: center;
115+
justify-content: center;
116+
height: 1.5rem;
117+
padding: 0 0.375rem;
118+
border-radius: 0.3125rem;
119+
border: 0.0625rem solid #1f1f1f;
120+
font-size: 0.875rem;
121+
font-weight: 500;
122+
color: #1f1f1f;
123+
transition: all 0.3s;
124+
125+
&:hover {
126+
background-color: #1f1f1f;
127+
color: white;
128+
}
129+
130+
&:disabled {
131+
border: 0.0625rem solid #1f1f1f;
132+
color: #1f1f1f;
133+
}
134+
}
135+
`;

0 commit comments

Comments
 (0)