atomsystem 0.1.38 → 0.1.39
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/app/templates/Button/Button.tsx +7 -3
- package/app/templates/Button/button.css +106 -46
- package/app/templates/Dropdown/Dropdown.css +112 -0
- package/app/templates/Dropdown/Dropdown.tsx +103 -0
- package/app/templates/Form/Form.tsx +1 -1
- package/app/templates/Navigation/Navigation.tsx +34 -287
- package/app/templates/Navigation/navigation.css +11 -64
- package/app/templates/Sidemenu/Sidemenu.tsx +55 -0
- package/app/templates/Sidemenu/sidemenu.css +4 -0
- package/build/css/_variables.css +250 -366
- package/build/typescript/theme.ts +5 -13
- package/cli/add.js +3 -0
- package/css/text.css +11 -18
- package/package.json +5 -2
- package/tokens/tokens.json +266 -238
|
@@ -1,306 +1,53 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
2
3
|
import Link from "next/link";
|
|
3
|
-
import Image from "next/image";
|
|
4
|
-
import { useEffect, useState } from "react";
|
|
5
4
|
import { usePathname } from "next/navigation";
|
|
6
|
-
import clsx from "clsx";
|
|
7
|
-
import { useMediaQuery } from "react-responsive";
|
|
8
5
|
|
|
9
|
-
interface
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
name: string;
|
|
13
|
-
path?: string;
|
|
14
|
-
}[];
|
|
6
|
+
// Navigation을 베이스 interface로 정의
|
|
7
|
+
export interface Navigation {
|
|
8
|
+
children: React.ReactNode;
|
|
15
9
|
}
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const el = document.getElementById(targetId);
|
|
21
|
-
if (el) {
|
|
22
|
-
const isHidden = el.style.display === "none" || el.style.display === "";
|
|
23
|
-
el.style.display = isHidden ? "block" : "none";
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const Navigation = ({
|
|
28
|
-
children,
|
|
29
|
-
id,
|
|
30
|
-
className,
|
|
31
|
-
type = "topnav",
|
|
32
|
-
}: {
|
|
33
|
-
children: React.ReactNode;
|
|
34
|
-
id?: string;
|
|
35
|
-
className?: string;
|
|
36
|
-
type?: "topnav" | "sidenav";
|
|
37
|
-
}) => {
|
|
38
|
-
const isMobile = useMediaQuery({ maxWidth: 767 });
|
|
11
|
+
export interface NavLink extends Navigation {
|
|
12
|
+
href: string;
|
|
13
|
+
}
|
|
39
14
|
|
|
40
|
-
const mobileClose = () => {
|
|
41
|
-
if (isMobile) {
|
|
42
|
-
if (id) {
|
|
43
|
-
handleNavToggle(id);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
15
|
|
|
16
|
+
const Navigation = ({ children }: Navigation) => {
|
|
48
17
|
return (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{children}
|
|
55
|
-
</nav>
|
|
56
|
-
);
|
|
57
|
-
};
|
|
18
|
+
<nav className="nav-menu flex items-center justify-between">
|
|
19
|
+
{children}
|
|
20
|
+
</nav>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
58
23
|
|
|
59
|
-
const NavItems = ({ navMenu, type = "sidenav" }: NavItemsAttr) => {
|
|
60
|
-
const pathname = usePathname();
|
|
61
24
|
|
|
25
|
+
const NavBrand = ({children, href}: NavLink) => {
|
|
62
26
|
return (
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<li className="relative group" key={menu.name}>
|
|
67
|
-
<div className={clsx("flex justify-between items-center pr-4")}>
|
|
68
|
-
<Link
|
|
69
|
-
href={menu.path || "#"}
|
|
70
|
-
className={`${pathname === menu.path ? "nav-text-active" : "nav-text-standard"} nav-${type} block hover:opacity-80 transition-opacity nav-padding`}
|
|
71
|
-
>
|
|
72
|
-
{menu.name}
|
|
73
|
-
</Link>
|
|
74
|
-
</div>
|
|
75
|
-
</li>
|
|
76
|
-
);
|
|
77
|
-
})}
|
|
78
|
-
</ul>
|
|
79
|
-
);
|
|
80
|
-
};
|
|
27
|
+
<Link href={href} className="flex items-center">{children}</Link>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
81
30
|
|
|
82
|
-
const
|
|
83
|
-
children,
|
|
84
|
-
targetId,
|
|
85
|
-
className,
|
|
86
|
-
}: {
|
|
87
|
-
children: React.ReactNode;
|
|
88
|
-
targetId: string;
|
|
89
|
-
className?: string;
|
|
90
|
-
}) => {
|
|
31
|
+
const NavList = ({children}: Navigation) => {
|
|
91
32
|
return (
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
);
|
|
96
|
-
};
|
|
33
|
+
<ul className="flex items-center gap-0">{children}</ul>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
97
36
|
|
|
98
|
-
const Sidebar = ({ navMenu, type = "sidenav" }: NavItemsAttr) => {
|
|
99
|
-
return (
|
|
100
|
-
<nav
|
|
101
|
-
className={clsx(
|
|
102
|
-
`relative nav-container nav-${type} w-full md:w-[320px] h-full z-20 left-0 overflow-auto`
|
|
103
|
-
)}
|
|
104
|
-
>
|
|
105
|
-
<ul>
|
|
106
|
-
{navMenu.map((menu, idx) => {
|
|
107
|
-
return (
|
|
108
|
-
<li className="relative group" key={menu.name + idx}>
|
|
109
|
-
<div className={clsx("flex justify-between items-center pr-4")}>
|
|
110
|
-
<Link
|
|
111
|
-
href={menu.path || "#"}
|
|
112
|
-
className={clsx(
|
|
113
|
-
`block nav-text-standard nav-${type} hover:opacity-80 transition-opacity nav-padding`
|
|
114
|
-
)}
|
|
115
|
-
>
|
|
116
|
-
{menu.name}
|
|
117
|
-
</Link>
|
|
118
|
-
</div>
|
|
119
|
-
</li>
|
|
120
|
-
);
|
|
121
|
-
})}
|
|
122
|
-
</ul>
|
|
123
|
-
</nav>
|
|
124
|
-
);
|
|
125
|
-
};
|
|
126
37
|
|
|
127
|
-
const
|
|
128
|
-
type = "sidebar",
|
|
129
|
-
navMenu,
|
|
130
|
-
brand = true,
|
|
131
|
-
}: {
|
|
132
|
-
type?: "sidebar" | "topmenu" | "mobile";
|
|
133
|
-
navMenu: {
|
|
134
|
-
name: string;
|
|
135
|
-
path?: string;
|
|
136
|
-
subItems?: { name: string; path: string }[];
|
|
137
|
-
label?: boolean;
|
|
138
|
-
}[];
|
|
139
|
-
brand?: boolean;
|
|
140
|
-
}) => {
|
|
141
|
-
const [windowSize, setWindowSize] = useState<number>(); // 초기값 설정
|
|
142
|
-
const [toggleMenu, setToggleMenu] = useState<boolean>(true);
|
|
38
|
+
const NavItem = ({children, href}: NavLink) => {
|
|
143
39
|
const pathname = usePathname();
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
// 윈도우 사이즈 추적
|
|
147
|
-
useEffect(() => {
|
|
148
|
-
const handleResize = () => {
|
|
149
|
-
setWindowSize(window.innerWidth); // 윈도우 사이즈 업데이트
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
window.addEventListener("resize", handleResize); // 리사이즈 이벤트 리스너 추가
|
|
153
|
-
|
|
154
|
-
return () => window.removeEventListener("resize", handleResize); // 컴포넌트 언마운트 시 리스너 제거
|
|
155
|
-
}, [windowSize]);
|
|
40
|
+
const active = pathname === href;
|
|
156
41
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
setToggleMenu(false);
|
|
162
|
-
}
|
|
163
|
-
}, []);
|
|
164
|
-
|
|
165
|
-
const toggleSubMenu = (menuName: string) => {
|
|
166
|
-
setOpenSubMenu((prev) => ({
|
|
167
|
-
...prev,
|
|
168
|
-
[menuName]: !prev[menuName],
|
|
169
|
-
}));
|
|
170
|
-
};
|
|
42
|
+
return (
|
|
43
|
+
<Link href={href} className={`nav-item ${active ? "text-primary!" : ""}`}>{children}</Link>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
171
46
|
|
|
172
|
-
const navType = type === "sidebar" ? "sidenav" : "topnav";
|
|
173
47
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
)}
|
|
181
|
-
>
|
|
182
|
-
{/* brand */}
|
|
183
|
-
{brand ? (
|
|
184
|
-
<div className="brand">
|
|
185
|
-
<div className={`flex items-center justify-between`}>
|
|
186
|
-
<Link href={"/"}>
|
|
187
|
-
<Image
|
|
188
|
-
src={"/images/global/logo.png"}
|
|
189
|
-
alt="Logo"
|
|
190
|
-
width={180}
|
|
191
|
-
height={45}
|
|
192
|
-
draggable="false"
|
|
193
|
-
></Image>
|
|
194
|
-
</Link>
|
|
195
|
-
<button
|
|
196
|
-
className={`fixed shadow-md rounded-full nav-bg nav-${navType} border nav-border w-12 h-12 top-6 left-6 cursor-pointer z-50`}
|
|
197
|
-
onClick={() => setToggleMenu(!toggleMenu)}
|
|
198
|
-
>
|
|
199
|
-
<span
|
|
200
|
-
className={`material-symbols-outlined nav-text-standard nav-${navType}`}
|
|
201
|
-
style={{ fontSize: "16px" }}
|
|
202
|
-
>
|
|
203
|
-
menu
|
|
204
|
-
</span>
|
|
205
|
-
</button>
|
|
206
|
-
</div>
|
|
207
|
-
</div>
|
|
208
|
-
) : null}
|
|
209
|
-
{/* List */}
|
|
210
|
-
<ul
|
|
211
|
-
className={clsx("", {
|
|
212
|
-
"flex flex-row": type == "topmenu",
|
|
213
|
-
})}
|
|
214
|
-
>
|
|
215
|
-
{navMenu.map((menu, idx) => {
|
|
216
|
-
switch (true) {
|
|
217
|
-
case menu.label === true && type == "sidebar":
|
|
218
|
-
return (
|
|
219
|
-
<p
|
|
220
|
-
key={menu.name + idx}
|
|
221
|
-
className={`nav-text-sub nav-${navType} mt-2 mb-0 nav-padding`}
|
|
222
|
-
>
|
|
223
|
-
{menu.name}
|
|
224
|
-
</p>
|
|
225
|
-
);
|
|
226
|
-
default:
|
|
227
|
-
return (
|
|
228
|
-
<li className="relative group" key={menu.name}>
|
|
229
|
-
<div
|
|
230
|
-
className={clsx("flex justify-between items-center pr-4")}
|
|
231
|
-
>
|
|
232
|
-
<Link
|
|
233
|
-
href={menu.path || "#"}
|
|
234
|
-
className={clsx(
|
|
235
|
-
`block nav-text-standard nav-${navType} nav-padding`,
|
|
236
|
-
{
|
|
237
|
-
"nav-text-active":
|
|
238
|
-
(menu.path && menu.path == pathname) ||
|
|
239
|
-
menu.subItems?.some((sub) => sub.path === pathname),
|
|
240
|
-
}
|
|
241
|
-
)}
|
|
242
|
-
>
|
|
243
|
-
{menu.name}
|
|
244
|
-
</Link>
|
|
245
|
-
{menu.subItems && type == "sidebar" ? (
|
|
246
|
-
<button
|
|
247
|
-
className="cursor-pointer"
|
|
248
|
-
onClick={() => toggleSubMenu(menu.name)}
|
|
249
|
-
>
|
|
250
|
-
<span
|
|
251
|
-
className={clsx(
|
|
252
|
-
`material-symbols-outlined nav-text-sub nav-${navType} transition-transform`,
|
|
253
|
-
{
|
|
254
|
-
"rotate-90": openSubMenu[menu.name],
|
|
255
|
-
}
|
|
256
|
-
)}
|
|
257
|
-
style={{ fontSize: "16px" }}
|
|
258
|
-
>
|
|
259
|
-
keyboard_arrow_right
|
|
260
|
-
</span>
|
|
261
|
-
</button>
|
|
262
|
-
) : null}
|
|
263
|
-
</div>
|
|
264
|
-
{menu.subItems ? (
|
|
265
|
-
<ul
|
|
266
|
-
className={clsx(
|
|
267
|
-
"block",
|
|
268
|
-
{
|
|
269
|
-
[`hidden absolute top-[42px] group-hover:block hover:block nav-bg nav-${navType} border nav-border`]:
|
|
270
|
-
type == "topmenu",
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
hidden: !openSubMenu[menu.name] && type == "sidebar",
|
|
274
|
-
}
|
|
275
|
-
)}
|
|
276
|
-
>
|
|
277
|
-
{menu.subItems.map((submenu) => {
|
|
278
|
-
return (
|
|
279
|
-
<li key={submenu.name}>
|
|
280
|
-
<Link
|
|
281
|
-
className={clsx(
|
|
282
|
-
`block nav-text-standard nav-${navType} nav-padding`,
|
|
283
|
-
{
|
|
284
|
-
"nav-text-active": submenu.path == pathname,
|
|
285
|
-
},
|
|
286
|
-
{ "pl-12 ": type == "sidebar" }
|
|
287
|
-
)}
|
|
288
|
-
href={submenu.path}
|
|
289
|
-
>
|
|
290
|
-
{submenu.name}
|
|
291
|
-
</Link>
|
|
292
|
-
</li>
|
|
293
|
-
);
|
|
294
|
-
})}
|
|
295
|
-
</ul>
|
|
296
|
-
) : null}
|
|
297
|
-
</li>
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
})}
|
|
301
|
-
</ul>
|
|
302
|
-
</nav>
|
|
303
|
-
);
|
|
304
|
-
};
|
|
305
|
-
export default Navigation;
|
|
306
|
-
export { Sidebar, GlobalNav, NavToggleButton, NavItems };
|
|
48
|
+
export {
|
|
49
|
+
Navigation,
|
|
50
|
+
NavBrand,
|
|
51
|
+
NavList,
|
|
52
|
+
NavItem
|
|
53
|
+
}
|
|
@@ -1,69 +1,16 @@
|
|
|
1
|
+
.nav-menu{
|
|
2
|
+
background-color: var(--bg-card);
|
|
3
|
+
border-bottom: 1px solid var(--border-standard);
|
|
4
|
+
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
1
5
|
|
|
2
|
-
|
|
3
|
-
.nav-container.nav-topnav {
|
|
4
|
-
background-color: var(--bg-topnav);
|
|
5
|
-
color: var(--text-topnav-standard);
|
|
6
|
-
border-color: var(--border-topnav);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.nav-container.nav-sidenav {
|
|
10
|
-
background-color: var(--bg-sidenav);
|
|
11
|
-
color: var(--text-sidenav-standard);
|
|
12
|
-
border-color: var(--border-sidenav);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.nav-bg.nav-topnav {
|
|
16
|
-
background-color: var(--bg-topnav);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.nav-bg.nav-sidenav {
|
|
20
|
-
background-color: var(--bg-sidenav);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.nav-border.nav-topnav {
|
|
24
|
-
border-color: var(--border-topnav);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.nav-border.nav-sidenav {
|
|
28
|
-
border-color: var(--border-sidenav);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.nav-text-standard.nav-topnav {
|
|
32
|
-
color: var(--text-topnav-standard);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.nav-text-standard.nav-sidenav {
|
|
36
|
-
color: var(--text-sidenav-standard);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.nav-text-active.nav-topnav {
|
|
40
|
-
color: var(--text-topnav-active);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.nav-text-active.nav-sidenav {
|
|
44
|
-
color: var(--text-sidenav-active);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.nav-text-sub.nav-topnav {
|
|
48
|
-
color: var(--text-topnav-sub);
|
|
49
6
|
}
|
|
50
7
|
|
|
51
|
-
.nav-
|
|
52
|
-
|
|
8
|
+
.nav-item{
|
|
9
|
+
border-radius: var(--radius-common);
|
|
10
|
+
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
11
|
+
color: var(--text-standard);
|
|
53
12
|
}
|
|
54
13
|
|
|
55
|
-
.nav-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.nav-padding.nav-sidenav {
|
|
60
|
-
padding: var(--spacing-sidenav-y) var(--spacing-sidenav-x);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.nav-gap.nav-topnav {
|
|
64
|
-
gap: var(--spacing-topnav-gap);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.nav-gap.nav-sidenav {
|
|
68
|
-
gap: var(--spacing-sidenav-gap);
|
|
69
|
-
}
|
|
14
|
+
.nav-item:hover{
|
|
15
|
+
background-color: var(--bg-hover);
|
|
16
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { usePathname } from "next/navigation";
|
|
5
|
+
|
|
6
|
+
// Navigation을 베이스 interface로 정의
|
|
7
|
+
export interface Sidemenu {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
minWidth?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface NavLink {
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
href: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const Sidemenu = ({ children, minWidth }: Sidemenu) => {
|
|
19
|
+
return (
|
|
20
|
+
<aside className={`nav-side flex flex-col overflow-auto h-full`} style={{minWidth: `${minWidth}px`}}>
|
|
21
|
+
{children}
|
|
22
|
+
</aside>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const NavBrand = ({children, href}: NavLink) => {
|
|
28
|
+
return (
|
|
29
|
+
<Link href={href} className="flex items-center">{children}</Link>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const NavList = ({children}: Sidemenu) => {
|
|
34
|
+
return (
|
|
35
|
+
<ul className="nav-list flex flex-col">{children}</ul>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
const NavItem = ({children, href}: NavLink) => {
|
|
41
|
+
const pathname = usePathname();
|
|
42
|
+
const active = pathname === href;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Link href={href} className={`nav-item text-nowrap ${active ? "text-primary!" : ""}`}>{children}</Link>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
Sidemenu,
|
|
52
|
+
NavBrand,
|
|
53
|
+
NavList,
|
|
54
|
+
NavItem
|
|
55
|
+
}
|