@edu-tosel/design 1.0.172 → 1.0.174

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/layout/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { default as Action } from "./template/Action";
2
2
  export { default as About } from "./template/About";
3
3
  export { default as Announcement } from "./template/Announcement";
4
4
  export { default as Archive } from "./template/Archive";
5
+ export { default as Books } from "./template/Books";
5
6
  export { default as Home } from "./template/home";
6
7
  export * from "./template/dashboard";
7
8
  export { default as Event } from "./template/Event";
@@ -18,6 +19,7 @@ export { default as TextBox } from "./template/TextBox";
18
19
  export { default as OlympiadLayout } from "./template/Olympiad/Olympiad.layout";
19
20
  export { default as Gomito } from "./template/Gomito";
20
21
  export { default as RegexamLayout } from "./template/Regexam/Regexam.layout";
22
+ export { default as BooksLayout } from "./template/Books/Books.layout";
21
23
  export { default as LegacyLayout } from "./template/Legacy/Legacy.layout";
22
24
  export { default as MonthlyProgressReport } from "./template/MonthlyProgressReport";
23
25
  export { default as Ticket } from "./template/Ticket";
package/layout/index.js CHANGED
@@ -2,6 +2,7 @@ export { default as Action } from "./template/Action";
2
2
  export { default as About } from "./template/About";
3
3
  export { default as Announcement } from "./template/Announcement";
4
4
  export { default as Archive } from "./template/Archive";
5
+ export { default as Books } from "./template/Books";
5
6
  export { default as Home } from "./template/home";
6
7
  export * from "./template/dashboard";
7
8
  export { default as Event } from "./template/Event";
@@ -18,6 +19,7 @@ export { default as TextBox } from "./template/TextBox";
18
19
  export { default as OlympiadLayout } from "./template/Olympiad/Olympiad.layout";
19
20
  export { default as Gomito } from "./template/Gomito";
20
21
  export { default as RegexamLayout } from "./template/Regexam/Regexam.layout";
22
+ export { default as BooksLayout } from "./template/Books/Books.layout";
21
23
  export { default as LegacyLayout } from "./template/Legacy/Legacy.layout";
22
24
  export { default as MonthlyProgressReport } from "./template/MonthlyProgressReport";
23
25
  export { default as Ticket } from "./template/Ticket";
@@ -1,6 +1,7 @@
1
- import { State } from "../../../interface";
2
- export default function Header({ state, seriesState, levelState, }: {
1
+ import { Button as _Button, State } from "../../../interface";
2
+ export default function Header({ state, seriesState, levelState, contents, }: {
3
3
  state: State<string>;
4
4
  seriesState: State<string | undefined>;
5
5
  levelState: State<string | undefined>;
6
+ contents: _Button[];
6
7
  }): import("react/jsx-runtime").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "../../../util";
3
3
  import { Input, Select } from "../../../widget";
4
4
  import { useResponsive } from "../../../hook";
5
- export default function Header({ state, seriesState, levelState, }) {
5
+ export default function Header({ state, seriesState, levelState, contents, }) {
6
6
  const container = {
7
7
  displays: "flex justify-between items-start flex-wrap flex-none gap-y-5 gap-x-5",
8
8
  sizes: "w-full h-fit",
@@ -51,28 +51,7 @@ export default function Header({ state, seriesState, levelState, }) {
51
51
  title: "High Junior",
52
52
  value: "HJ",
53
53
  },
54
- ], state: levelState, placeholder: "\uB808\uBCA8" }) })] }), _jsx(ContentBox, { contents: [
55
- {
56
- title: "교재 음원",
57
- onClick: () => { },
58
- disabled: false,
59
- },
60
- {
61
- title: "정오표",
62
- onClick: () => { },
63
- disabled: true,
64
- },
65
- {
66
- title: "홍보자료",
67
- onClick: () => { },
68
- disabled: true,
69
- },
70
- {
71
- title: "기타 자료",
72
- onClick: () => { },
73
- disabled: true,
74
- },
75
- ] })] }));
54
+ ], state: levelState, placeholder: "\uB808\uBCA8" }) })] }), _jsx(ContentBox, { contents: contents })] }));
76
55
  }
77
56
  function ContentBox({ contents }) {
78
57
  const container = {
@@ -0,0 +1 @@
1
+ export default function BooksLayout(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import { cn } from "../../../util";
4
+ import { Books } from "../../../layout";
5
+ export default function BooksLayout() {
6
+ const ref = useRef(null);
7
+ const container = {
8
+ sizes: "min-h-screen w-full",
9
+ textoptions: "break-keep antialiased",
10
+ cursor: "cursor-default",
11
+ };
12
+ return (_jsx("div", { className: cn(container), children: _jsx(Books.SectionA, {}) }));
13
+ }
@@ -0,0 +1,2 @@
1
+ export default function SectionA(): import("react/jsx-runtime").JSX.Element;
2
+ export declare function ScrollBookComponent(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,313 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
3
+ import gsap from "gsap";
4
+ import ScrollTrigger from "gsap/ScrollTrigger";
5
+ import { cn } from "../../../util";
6
+ import { useResponsive } from "../../../hook";
7
+ gsap.registerPlugin(ScrollTrigger);
8
+ let divIndex = 0;
9
+ export default function SectionA() {
10
+ const stickyRef = useRef(null);
11
+ const bookRef = useRef(null);
12
+ const mainTitleRef = useRef(null);
13
+ const subTitleRef = useRef(null);
14
+ const bgRef = useRef(null);
15
+ const buttonRef = useRef(null);
16
+ const bookScrollRef = useRef(null);
17
+ const refs = useRef([]);
18
+ const isMD = useResponsive("md");
19
+ const cardHeight = isMD ? 560 : 400;
20
+ console.log(isMD);
21
+ console.log(cardHeight);
22
+ const handleScroll = (direction) => {
23
+ if (bookScrollRef.current) {
24
+ const scrollAmount = cardHeight;
25
+ const currentScrollTop = bookScrollRef.current.scrollTop;
26
+ console.log(`direction: ${direction}`);
27
+ const newScrollTop = direction === "up"
28
+ ? Math.max(currentScrollTop - scrollAmount, 0)
29
+ : currentScrollTop + scrollAmount;
30
+ bookScrollRef.current.scrollTo({
31
+ top: newScrollTop,
32
+ behavior: "smooth",
33
+ });
34
+ }
35
+ };
36
+ useEffect(() => {
37
+ const ctx = gsap.context(() => {
38
+ const vh = window.innerHeight * 0.01;
39
+ const scrollWrapper = stickyRef.current;
40
+ const bookWrapper = bookRef.current;
41
+ const sectionEndPoint = `${50 * vh}px`;
42
+ const bookScrollEndPoint = `${1000 * vh}px`;
43
+ if (scrollWrapper && bookWrapper) {
44
+ // 첫 번째 ScrollTrigger: stickyRef를 고정하고 mainTitle의 투명도와 크기 조정
45
+ const scrollTrigger = ScrollTrigger.create({
46
+ trigger: scrollWrapper,
47
+ start: "top top",
48
+ end: `bottom ${sectionEndPoint}`,
49
+ pin: true,
50
+ markers: false,
51
+ scrub: true,
52
+ });
53
+ // mainTitle의 투명도와 크기를 스크롤 위치에 따라 조절하는 애니메이션
54
+ if (mainTitleRef.current) {
55
+ gsap.to(mainTitleRef.current, {
56
+ opacity: 0,
57
+ scale: 0.7, // 크기를 70%로 줄임
58
+ scrollTrigger: {
59
+ trigger: scrollWrapper,
60
+ start: `top-=10 top`,
61
+ end: `bottom ${sectionEndPoint}`,
62
+ markers: false,
63
+ scrub: true,
64
+ },
65
+ });
66
+ }
67
+ if (bgRef.current) {
68
+ gsap.to(bgRef.current, {
69
+ opacity: 0,
70
+ scrollTrigger: {
71
+ trigger: scrollWrapper,
72
+ start: `top-=10 top`,
73
+ end: `bottom ${sectionEndPoint}`,
74
+ markers: false,
75
+ scrub: true,
76
+ },
77
+ });
78
+ }
79
+ if (buttonRef.current) {
80
+ const tween = gsap.to(buttonRef.current, {
81
+ bottom: "60px",
82
+ duration: 0.3,
83
+ ease: "power2.inOut",
84
+ scrollTrigger: {
85
+ trigger: "",
86
+ start: `top+=1 top-=1`,
87
+ end: `bottom bottom`,
88
+ markers: false,
89
+ toggleActions: "play play play reverse",
90
+ },
91
+ });
92
+ // 스크롤에 따라 책 이미지 전환
93
+ refs.current.forEach((ref) => {
94
+ if (ref) {
95
+ gsap.to(ref, {
96
+ scrollTrigger: {
97
+ trigger: ref,
98
+ start: "top top",
99
+ end: "bottom top",
100
+ snap: {
101
+ snapTo: 1,
102
+ duration: 0.5,
103
+ ease: "power3.inOut",
104
+ },
105
+ onEnter: () => {
106
+ divIndex++;
107
+ handleScroll("down");
108
+ console.log(`onEnter: ${divIndex}`);
109
+ },
110
+ onLeaveBack: () => {
111
+ divIndex--;
112
+ handleScroll("up");
113
+ console.log(`onLeaveBack: ${divIndex}`);
114
+ },
115
+ },
116
+ });
117
+ }
118
+ });
119
+ }
120
+ // 두 번째 ScrollTrigger: bookWrapper가 뷰포트 상단에 도달하면 sticky하게 붙기
121
+ const bookTrigger = ScrollTrigger.create({
122
+ trigger: bookWrapper,
123
+ start: "top top",
124
+ end: `${bookScrollEndPoint} top`,
125
+ pin: true,
126
+ pinSpacing: false,
127
+ markers: true,
128
+ });
129
+ return () => {
130
+ scrollTrigger.kill();
131
+ bookTrigger.kill();
132
+ };
133
+ }
134
+ });
135
+ return () => ctx.kill();
136
+ }, [handleScroll]);
137
+ const container = {
138
+ sizes: "w-full h-fit relative",
139
+ displays: "flex flex-col justify-start items-center",
140
+ textstyles: "antialiased",
141
+ backgrounds: "bg-white bg-cover bg-center",
142
+ };
143
+ const absoluteWrapperBottom = {
144
+ sizes: "w-full h-[200vh]",
145
+ displays: "absolute flex flex-col",
146
+ positions: "top-0 left-0 z-0",
147
+ };
148
+ const absoluteWrapperTop = {
149
+ sizes: "w-full h-[100vh]",
150
+ displays: "flex flex-col justify-center items-center fixed",
151
+ positions: "top-0 left-0 z-100",
152
+ };
153
+ const buttonStyling = {
154
+ sizes: "w-fit h-fit rounded-2xl",
155
+ spacings: "p-4 absolute mt-40",
156
+ display: "flex flex-row gap-2",
157
+ textStyling: "font-bold text-green-dark",
158
+ backgrounds: "shadow-main bg-white",
159
+ animation: "duration-300 hover:bg-green-dark hover:text-white",
160
+ };
161
+ const TitleWrapper = {
162
+ spacings: "pt-[20vh] z-5",
163
+ sizes: "w-full h-[100vh]",
164
+ display: "flex flex-col justify-start items-center",
165
+ };
166
+ const miniTitle = {
167
+ textstyles: "text-center font-bold text-xl text-white",
168
+ };
169
+ const mainTitle = {
170
+ display: "flex flex-row gap-4 md:gap-10 mt-[5vh]",
171
+ textstyles: "text-center font-medium text-5xl md:text-8xl ml:text-10xl text-white leading-none",
172
+ };
173
+ const bookWrapper = {
174
+ displays: "flex flex-col justify-center items-center -translate-y-[40vh]",
175
+ sizes: "p-10 w-full h-screen ",
176
+ };
177
+ const bookExplainWrapper = {
178
+ displays: "flex flex-col md:flex-row justify-center items-center",
179
+ sizes: "h-screen w-full",
180
+ };
181
+ const bookTitle = {
182
+ displays: "flex flex-col justify-end items-center",
183
+ sizes: "h-full w-full pb-5",
184
+ textStyling: "font-bold text-2xl",
185
+ };
186
+ return (_jsxs("div", { className: cn(container), children: [_jsx("div", { className: cn(absoluteWrapperBottom, "bg-gradient-to-tr from-crimson-burgundy to-green-dark"), ref: bgRef }), _jsx("div", { className: cn(absoluteWrapperTop), children: _jsxs("div", { className: cn(buttonStyling), ref: buttonRef, onClick: () => {
187
+ window.location.href = "https://smartstore.naver.com/tosel";
188
+ }, children: [_jsx("div", { children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "size-6", children: _jsx("path", { d: "M2.25 2.25a.75.75 0 0 0 0 1.5h1.386c.17 0 .318.114.362.278l2.558 9.592a3.752 3.752 0 0 0-2.806 3.63c0 .414.336.75.75.75h15.75a.75.75 0 0 0 0-1.5H5.378A2.25 2.25 0 0 1 7.5 15h11.218a.75.75 0 0 0 .674-.421 60.358 60.358 0 0 0 2.96-7.228.75.75 0 0 0-.525-.965A60.864 60.864 0 0 0 5.68 4.509l-.232-.867A1.875 1.875 0 0 0 3.636 2.25H2.25ZM3.75 20.25a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM16.5 20.25a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0Z" }) }) }), "\uAD50\uC7AC\uBAB0 \uBC14\uB85C\uAC00\uAE30"] }) }), _jsxs("div", { className: cn(TitleWrapper), ref: stickyRef, children: [_jsx("div", { className: cn(miniTitle), ref: subTitleRef, children: "\uB2F9\uC2E0\uC774 \uBBFF\uC744 \uC218 \uC788\uB294 \uC601\uC5B4\uAD50\uC7AC" }), _jsxs("div", { className: cn(mainTitle), ref: mainTitleRef, children: [_jsx("div", { className: "flex justify-center items-center w-40 md:w-80 ml:w-120", children: _jsx("img", { src: "https://resource.tosel.co.kr/images/img-logo-tosel-darkmode.svg", alt: "", className: "max-w-full h-auto" }) }), _jsx("div", { className: "h-full w-0.5 bg-white" }), _jsx("div", { children: " books " })] })] }), _jsx("div", { className: cn(bookWrapper), ref: bookRef, children: _jsx("div", { className: cn("w-72 h-100 md:w-100 md:h-140 bg-white z-10 rounded-2xl shadow-main overflow-y-scroll scrollbar-hidden snap-y snap-mandatory"), ref: bookScrollRef, children: _jsx(ScrollBookComponent, {}) }) }), bookData.map((item, index) => {
189
+ // item.levelLow 값을 LevelKey로 변환하기 전에 유효성 검사
190
+ const lowLevelStyle = getLevelStyle(item.levelLow); // 안전하게 캐스팅
191
+ const highLevelStyle = getLevelStyle(item.levelHigh); // 이 부분도 마찬가지로
192
+ return (_jsxs("div", { className: cn(bookExplainWrapper), ref: (el) => {
193
+ if (el) {
194
+ refs.current[index] = el;
195
+ }
196
+ }, children: [_jsxs("div", { className: cn(bookTitle), children: [_jsx("div", { children: item.title }), _jsx("div", { className: "text-base text-gray-medium", children: item.type })] }), _jsx("div", { className: "h-100 w-full shrink-0" }), _jsxs("div", { className: "h-full w-full pt-5 flex flex-col justify-start items-center", children: [_jsxs("div", { className: "w-fit h-fit flex flex-row gap-2", children: [_jsx("div", { className: `w-fit h-fit px-2 py-1 text-sm rounded-md font-bold ${lowLevelStyle.textColor} ${lowLevelStyle.bgColor}`, children: lowLevelStyle.name }), "~", _jsx("div", { className: `w-fit h-fit px-2 py-1 text-sm rounded-md font-bold ${highLevelStyle.textColor} ${highLevelStyle.bgColor}`, children: highLevelStyle.name })] }), isMD && (_jsx("div", { className: "text-center font-medium text-sm", children: item.description }))] })] }, item.id));
197
+ })] }));
198
+ }
199
+ const bookData = [
200
+ {
201
+ id: 1,
202
+ type: "학습 시리즈",
203
+ title: "Speaking Series",
204
+ imgSrc: "https://resource.tosel.co.kr/images/SP-COVER-PS.png",
205
+ description: "어쩌",
206
+ levelLow: "PS",
207
+ levelHigh: "HJ",
208
+ link: "",
209
+ },
210
+ {
211
+ id: 2,
212
+ type: "학습 시리즈",
213
+ title: "Story Series",
214
+ imgSrc: "https://resource.tosel.co.kr/images/SP-COVER-PS.png",
215
+ description: "어쩌",
216
+ levelLow: "PS",
217
+ levelHigh: "JR",
218
+ link: "",
219
+ },
220
+ {
221
+ id: 3,
222
+ type: "학습 시리즈",
223
+ title: "Listening Series",
224
+ imgSrc: "https://resource.tosel.co.kr/images/SP-COVER-PS.png",
225
+ description: "어쩌",
226
+ levelLow: "PS",
227
+ levelHigh: "HJ",
228
+ link: "",
229
+ },
230
+ {
231
+ id: 4,
232
+ type: "학습 시리즈",
233
+ title: "Reading Series",
234
+ imgSrc: "https://resource.tosel.co.kr/images/SP-COVER-PS.png",
235
+ description: "어쩌",
236
+ levelLow: "PS",
237
+ levelHigh: "HJ",
238
+ link: "",
239
+ },
240
+ {
241
+ id: 5,
242
+ type: "학습 시리즈",
243
+ title: "Grammar Series",
244
+ imgSrc: "https://resource.tosel.co.kr/images/SP-COVER-PS.png",
245
+ description: "어쩌",
246
+ levelLow: "PS",
247
+ levelHigh: "HJ",
248
+ link: "",
249
+ },
250
+ {
251
+ id: 6,
252
+ type: "학습 시리즈",
253
+ title: "VOCA Series",
254
+ imgSrc: "https://resource.tosel.co.kr/images/SP-COVER-PS.png",
255
+ description: "어쩌",
256
+ levelLow: "PS",
257
+ levelHigh: "HJ",
258
+ link: "",
259
+ },
260
+ ];
261
+ const levelStyles = {
262
+ CO: {
263
+ name: "cocoon",
264
+ textColor: "text-cocoon-green",
265
+ bgColor: "bg-cocoon-green/10",
266
+ },
267
+ PS: {
268
+ name: "Pre-Starter",
269
+ textColor: "text-ps-pink",
270
+ bgColor: "bg-ps-pink-light",
271
+ },
272
+ ST: {
273
+ name: "Starter",
274
+ textColor: "text-st-orange",
275
+ bgColor: "bg-st-orange-light",
276
+ },
277
+ BA: {
278
+ name: "Basic",
279
+ textColor: "text-ba-yellow",
280
+ bgColor: "bg-ba-yellow-light",
281
+ },
282
+ JR: {
283
+ name: "Junior",
284
+ textColor: "text-jr-blue",
285
+ bgColor: "bg-jr-blue-light",
286
+ },
287
+ HJ: {
288
+ name: "High Junior",
289
+ textColor: "text-hj-blue",
290
+ bgColor: "bg-hj-blue-light",
291
+ },
292
+ AD: {
293
+ name: "Advanced",
294
+ textColor: "text-gray-dark",
295
+ bgColor: "bg-gray-light",
296
+ },
297
+ };
298
+ const getLevelStyle = (level) => {
299
+ return levelStyles[level];
300
+ };
301
+ export function ScrollBookComponent() {
302
+ const container = {
303
+ sizes: "w-full h-fit relative",
304
+ displyas: "flex flex-col",
305
+ backgrounds: "bg-white",
306
+ };
307
+ const imageWrapper = {
308
+ sizes: "w-72 h-100 md:w-100 md:h-140",
309
+ imgStyling: "bg-contain bg-no-repeat bg-center",
310
+ scrollActions: "snap-start",
311
+ };
312
+ return (_jsx("div", { className: cn(container), children: bookData.map((item) => (_jsx("div", { className: cn(imageWrapper, `bg-[url('${item.imgSrc}')]`) }, item.id))) }));
313
+ }
@@ -0,0 +1,5 @@
1
+ import SectionA from "./SectionA";
2
+ declare const Books: {
3
+ SectionA: typeof SectionA;
4
+ };
5
+ export default Books;
@@ -0,0 +1,5 @@
1
+ import SectionA from "./SectionA";
2
+ const Books = {
3
+ SectionA,
4
+ };
5
+ export default Books;
@@ -35,7 +35,7 @@ export default function OfflineExam() {
35
35
  const container = {
36
36
  displays: "flex flex-col justify-center items-center",
37
37
  sizes: "w-full h-fit min-h-screen",
38
- spacings: "pt-20 md:px-10 px-5 pb-20",
38
+ spacings: "pt-44 md:px-10 px-5 pb-20",
39
39
  backgrounds: "bg-gray-light/50",
40
40
  };
41
41
  //controlls max-w
@@ -1,6 +1,6 @@
1
1
  import { Button, OnClick } from "../../../../interface";
2
2
  import { ReactNode } from "react";
3
- export default function Layout({ contents, children, logo, rightButton, }: {
3
+ export default function Layout({ contents, children, logo, rightButton, option, }: {
4
4
  contents: Button[];
5
5
  children: React.ReactNode;
6
6
  logo: {
@@ -8,4 +8,7 @@ export default function Layout({ contents, children, logo, rightButton, }: {
8
8
  onClick?: OnClick;
9
9
  };
10
10
  rightButton?: Button;
11
+ option?: {
12
+ isPulled?: boolean;
13
+ };
11
14
  }): import("react/jsx-runtime").JSX.Element;
@@ -3,12 +3,13 @@ import { cn } from "../../../../util";
3
3
  import Footer from "./Footer";
4
4
  import Header from "./Header";
5
5
  import { useActionStore } from "../../../../store";
6
- export default function Layout({ contents, children, logo, rightButton, }) {
6
+ export default function Layout({ contents, children, logo, rightButton, option, }) {
7
+ const { isPulled } = option ?? {};
7
8
  const container = {
8
9
  displays: "flex flex-col",
9
10
  sizes: "w-full min-h-screen",
10
11
  overflows: "overflow-x-hidden ",
11
12
  };
12
13
  const { setFlag } = useActionStore();
13
- return (_jsxs("div", { className: cn(container), onClick: setFlag, children: [_jsx(Header, { logo: logo, rightButton: rightButton, contents: contents }), _jsx("div", { className: "w-full max-w-[1200px] h-15" }), children, _jsx(Footer, {})] }));
14
+ return (_jsxs("div", { className: cn(container), onClick: setFlag, children: [_jsx(Header, { logo: logo, rightButton: rightButton, contents: contents }), isPulled && _jsx("div", { className: "w-full max-w-[1200px] h-15" }), children, _jsx(Footer, {})] }));
14
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edu-tosel/design",
3
- "version": "1.0.172",
3
+ "version": "1.0.174",
4
4
  "description": "UI components for International TOSEL Committee",
5
5
  "keywords": [
6
6
  "jsx",
package/version.txt CHANGED
@@ -1 +1 @@
1
- 1.0.172
1
+ 1.0.174