atomsystem 0.1.17 โ 0.1.19
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 +6 -6
- package/app/templates/Card/Card.tsx +1 -1
- package/app/templates/CodeBlock/CodeBlock.tsx +28 -6
- package/app/templates/GlobalNav/GlobalNav.tsx +30 -38
- package/app/templates/Input/Input.tsx +123 -119
- package/app/templates/TOC/TOC.tsx +60 -0
- package/app/templates/Tab/Tab.tsx +93 -0
- package/app/templates/Theme/Theme.tsx +22 -22
- package/app/templates/Toast/Toast.tsx +106 -0
- package/cli/init.js +0 -15
- package/package.json +59 -51
- package/token.json +154 -1115
- package/tailwind.config.ts +0 -160
- package/tokens/buildMergeCss.js +0 -87
- package/tokens/buildTokenSplit.js +0 -81
- package/tokens/setorder/token_dark.json +0 -390
- package/tokens/setorder/token_digitalFont.json +0 -6
- package/tokens/setorder/token_global.json +0 -888
- package/tokens/token/dark.json +0 -904
- package/tokens/token/digitalFont.json +0 -882
- package/tokens/token/global.json +0 -882
- package/tokens/token/inputCircle.json +0 -888
- package/tokens/token/inputStandard.json +0 -888
- package/tokens/token/standard.json +0 -976
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import gsap from "gsap";
|
|
5
|
+
import { useGSAP } from "@gsap/react";
|
|
6
|
+
|
|
7
|
+
const Tab = ({
|
|
8
|
+
tabs,
|
|
9
|
+
tabClass,
|
|
10
|
+
tabNavItemClass,
|
|
11
|
+
tabNavItemActiveClass,
|
|
12
|
+
tabContents,
|
|
13
|
+
tabContentClass,
|
|
14
|
+
tabContentItem,
|
|
15
|
+
expectionValue,
|
|
16
|
+
expectionLayout,
|
|
17
|
+
}: {
|
|
18
|
+
tabs: Array<{ id: string; target: string[]; tabItem: React.ReactNode }>;
|
|
19
|
+
tabClass?: string;
|
|
20
|
+
tabNavItemClass?: string;
|
|
21
|
+
tabNavItemActiveClass?: string;
|
|
22
|
+
tabContents: Array<{ id: string; tag: string; content: React.ReactNode }>;
|
|
23
|
+
tabContentClass?: string;
|
|
24
|
+
tabContentItem?: string;
|
|
25
|
+
expectionValue?: string;
|
|
26
|
+
expectionLayout?: string;
|
|
27
|
+
}) => {
|
|
28
|
+
const [activeContent, setActiveContent] = useState(tabs[0].target);
|
|
29
|
+
const [activeTab, setActiveTab] = useState(tabs[0].id);
|
|
30
|
+
const tabContentRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
|
|
32
|
+
const handleTabActive = (target: string[], id: string) => {
|
|
33
|
+
setActiveContent(target);
|
|
34
|
+
setActiveTab(id);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// ๐ activeContent๊ฐ ๋ณ๊ฒฝ๋ ์ดํ์ ์คํ
|
|
38
|
+
useGSAP(() => {
|
|
39
|
+
if (tabContentRef.current) {
|
|
40
|
+
gsap.fromTo(
|
|
41
|
+
tabContentRef.current.children,
|
|
42
|
+
{ y: 50, opacity: 0 },
|
|
43
|
+
{
|
|
44
|
+
y: 0,
|
|
45
|
+
opacity: 1,
|
|
46
|
+
stagger: 0.1,
|
|
47
|
+
duration: 0.6,
|
|
48
|
+
ease: "power4.out",
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}, [activeContent]);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="flex flex-col">
|
|
56
|
+
<div className={tabClass}>
|
|
57
|
+
{tabs.map((tab) => {
|
|
58
|
+
return (
|
|
59
|
+
<button
|
|
60
|
+
key={tab.id}
|
|
61
|
+
id={tab.id}
|
|
62
|
+
onClick={() => handleTabActive(tab.target, tab.id)}
|
|
63
|
+
className={`cursor-pointer ${tabNavItemClass} ${
|
|
64
|
+
activeTab == tab.id
|
|
65
|
+
? `text-primary ${tabNavItemActiveClass}`
|
|
66
|
+
: ""
|
|
67
|
+
}`}
|
|
68
|
+
>
|
|
69
|
+
{tab.tabItem}
|
|
70
|
+
</button>
|
|
71
|
+
);
|
|
72
|
+
})}
|
|
73
|
+
</div>
|
|
74
|
+
<div ref={tabContentRef} className={tabContentClass}>
|
|
75
|
+
{tabContents.map((tabContent) => {
|
|
76
|
+
return activeContent.includes(tabContent.tag) ? (
|
|
77
|
+
<div
|
|
78
|
+
key={tabContent.id}
|
|
79
|
+
id={tabContent.tag}
|
|
80
|
+
className={`w-full sm:w-auto ${tabContentItem} ${
|
|
81
|
+
activeTab == expectionValue ? expectionLayout : null
|
|
82
|
+
}`}
|
|
83
|
+
>
|
|
84
|
+
{tabContent.content}
|
|
85
|
+
</div>
|
|
86
|
+
) : null;
|
|
87
|
+
})}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default Tab;
|
|
@@ -8,22 +8,22 @@ export const ThemeSelector = ({
|
|
|
8
8
|
}: {
|
|
9
9
|
type: "all" | "theme" | "input";
|
|
10
10
|
}) => {
|
|
11
|
-
const themes = ["
|
|
11
|
+
const themes = ["light", "dark"];
|
|
12
12
|
const [selectedTheme, setSelectedTheme] = useState<string | null>(null);
|
|
13
|
-
const inputThemes = ["inputStandard", "inputCircle"];
|
|
14
|
-
const [selectedInputTheme, setSelectedInputTheme] = useState<string | null>(
|
|
15
|
-
|
|
16
|
-
);
|
|
13
|
+
// const inputThemes = ["inputStandard", "inputCircle"];
|
|
14
|
+
// const [selectedInputTheme, setSelectedInputTheme] = useState<string | null>(
|
|
15
|
+
// null
|
|
16
|
+
// );
|
|
17
17
|
const [isThemeLoading, setThemeLoading] = useState(false);
|
|
18
18
|
|
|
19
19
|
useEffect(() => {
|
|
20
20
|
const chageThemeValue = () => {
|
|
21
|
-
const storedTheme = localStorage.getItem("theme") || "
|
|
22
|
-
const storedInputTheme =
|
|
23
|
-
|
|
21
|
+
const storedTheme = localStorage.getItem("theme") || "light";
|
|
22
|
+
// const storedInputTheme =
|
|
23
|
+
// localStorage.getItem("inputTheme") || "inputStandard";
|
|
24
24
|
|
|
25
25
|
setSelectedTheme(storedTheme);
|
|
26
|
-
setSelectedInputTheme(storedInputTheme);
|
|
26
|
+
// setSelectedInputTheme(storedInputTheme);
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
chageThemeValue();
|
|
@@ -39,13 +39,13 @@ export const ThemeSelector = ({
|
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
// input theme
|
|
42
|
-
const handleInputThemeChange = (theme: string) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
};
|
|
42
|
+
// const handleInputThemeChange = (theme: string) => {
|
|
43
|
+
// if (typeof window !== "undefined") {
|
|
44
|
+
// localStorage.setItem("inputTheme", theme);
|
|
45
|
+
// window.dispatchEvent(new Event("storage"));
|
|
46
|
+
// setSelectedInputTheme(theme);
|
|
47
|
+
// }
|
|
48
|
+
// };
|
|
49
49
|
if (isThemeLoading == false) {
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
@@ -67,7 +67,7 @@ export const ThemeSelector = ({
|
|
|
67
67
|
))}
|
|
68
68
|
</div>
|
|
69
69
|
) : null}
|
|
70
|
-
{type == "all" || type == "input" ? (
|
|
70
|
+
{/* {type == "all" || type == "input" ? (
|
|
71
71
|
<div className="flex gap-4">
|
|
72
72
|
{inputThemes.map((inputTheme) => (
|
|
73
73
|
<Radio
|
|
@@ -79,7 +79,7 @@ export const ThemeSelector = ({
|
|
|
79
79
|
/>
|
|
80
80
|
))}
|
|
81
81
|
</div>
|
|
82
|
-
) : null}
|
|
82
|
+
) : null} */}
|
|
83
83
|
</div>
|
|
84
84
|
</div>
|
|
85
85
|
);
|
|
@@ -99,8 +99,10 @@ export const ThemeWrapper = ({
|
|
|
99
99
|
useEffect(() => {
|
|
100
100
|
// ๋ก์ปฌ ์คํ ๋ฆฌ์ง์์ ํ
๋ง ๊ฐ์ ธ์ค๊ธฐ
|
|
101
101
|
const listenStorageChange = () => {
|
|
102
|
-
setTheme(localStorage.getItem("theme") || "
|
|
102
|
+
setTheme(localStorage.getItem("theme") || "light");
|
|
103
103
|
setInputTheme(localStorage.getItem("inputTheme") || "inputStandard");
|
|
104
|
+
|
|
105
|
+
document.body.className = `${className} ${theme} ${inputTheme}`;
|
|
104
106
|
};
|
|
105
107
|
listenStorageChange();
|
|
106
108
|
window.addEventListener("storage", listenStorageChange);
|
|
@@ -112,7 +114,5 @@ export const ThemeWrapper = ({
|
|
|
112
114
|
return null;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
return
|
|
116
|
-
<body className={`${className} ${theme} ${inputTheme}`}>{children}</body>
|
|
117
|
-
);
|
|
117
|
+
return <>{children}</>;
|
|
118
118
|
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, useRef } from "react";
|
|
4
|
+
import gsap from "gsap";
|
|
5
|
+
import { useGSAP } from "@gsap/react";
|
|
6
|
+
|
|
7
|
+
gsap.registerPlugin(useGSAP);
|
|
8
|
+
|
|
9
|
+
// โ
Toast Message ํ์
์ ์
|
|
10
|
+
export type ToastMessage = {
|
|
11
|
+
id: string;
|
|
12
|
+
status: "success" | "error";
|
|
13
|
+
message: string;
|
|
14
|
+
desc?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// โ
์ ์ญ ๋ฉ์์ง ์ ์ฅ์์ ๊ตฌ๋
์์คํ
|
|
18
|
+
let messages: ToastMessage[] = [];
|
|
19
|
+
let listener: ((messages: ToastMessage[]) => void) | null = null;
|
|
20
|
+
|
|
21
|
+
export const addToast = (msg: Omit<ToastMessage, "id">) => {
|
|
22
|
+
const id = crypto.randomUUID();
|
|
23
|
+
messages = [...messages, { id, ...msg }];
|
|
24
|
+
// messages ๊ฐ listener์ ๋ฐ์
|
|
25
|
+
listener?.(messages);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const removeToast = (id: string) => {
|
|
29
|
+
messages = messages.filter((m) => m.id !== id);
|
|
30
|
+
listener?.(messages);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ์ฝ๋ฐฑ์ผ๋ก message value ์ถ์ ์
|
|
34
|
+
const subscribeToast = (cb: (messages: ToastMessage[]) => void) => {
|
|
35
|
+
listener = cb;
|
|
36
|
+
cb(messages);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const unsubscribeToast = () => {
|
|
40
|
+
listener = null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// โ
useToast ํ
์ ๊ณต
|
|
44
|
+
export const useToast = () => {
|
|
45
|
+
return addToast;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// โ
ToastContainer ์ปดํฌ๋ํธ (๋ด๋ถ ToastItem ํฌํจ)
|
|
49
|
+
export const Toast = () => {
|
|
50
|
+
const [messages, setMessages] = useState<ToastMessage[]>([]);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
subscribeToast(setMessages);
|
|
54
|
+
return () => unsubscribeToast();
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="fixed flex flex-col gap-2 bottom-[24px] right-[24px] z-50">
|
|
59
|
+
{messages.map((message) => (
|
|
60
|
+
<ToastItem
|
|
61
|
+
key={message.id}
|
|
62
|
+
message={message}
|
|
63
|
+
remove={() => removeToast(message.id)}
|
|
64
|
+
/>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const ToastItem = ({
|
|
71
|
+
message,
|
|
72
|
+
remove,
|
|
73
|
+
}: {
|
|
74
|
+
message: ToastMessage;
|
|
75
|
+
remove: () => void;
|
|
76
|
+
}) => {
|
|
77
|
+
const elRef = useRef<HTMLDivElement>(null);
|
|
78
|
+
|
|
79
|
+
useGSAP(() => {
|
|
80
|
+
if (!elRef.current) return;
|
|
81
|
+
|
|
82
|
+
const tl = gsap.timeline({ onComplete: remove });
|
|
83
|
+
tl.from(elRef.current, {
|
|
84
|
+
opacity: 0,
|
|
85
|
+
y: 50,
|
|
86
|
+
ease: "power4.out",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
tl.to(elRef.current, {
|
|
90
|
+
delay: 3,
|
|
91
|
+
y: 50,
|
|
92
|
+
opacity: 0,
|
|
93
|
+
ease: "power4.out",
|
|
94
|
+
});
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
ref={elRef}
|
|
100
|
+
className="bg-white/20 border border-primary rounded text-white p-4 shadow-md min-w-[250px]"
|
|
101
|
+
>
|
|
102
|
+
<p>{message.message}</p>
|
|
103
|
+
{message.desc ? <p className="text-sm">{message.desc}</p> : null}
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
};
|
package/cli/init.js
CHANGED
|
@@ -15,12 +15,10 @@ const __dirname = path.dirname(__filename);
|
|
|
15
15
|
// ํจํค์ง ๋ด๋ถ ๊ฒฝ๋ก ์ค์
|
|
16
16
|
const packageRoot = path.join(__dirname, "..");
|
|
17
17
|
const tokenFile = path.join(packageRoot, "token.json");
|
|
18
|
-
const tokenDir = path.join(packageRoot, "tokens");
|
|
19
18
|
|
|
20
19
|
// ํ๋ก์ ํธ ๋ฃจํธ ๊ฒฝ๋ก (์ฌ์ฉ์๊ฐ ์คํํ ๊ณณ)
|
|
21
20
|
const projectRoot = process.cwd();
|
|
22
21
|
const targetTokenFile = path.join(projectRoot, "token.json");
|
|
23
|
-
const targetTokensDir = path.join(projectRoot, "tokens");
|
|
24
22
|
|
|
25
23
|
// ์ฌ์ฉ์์๊ฒ Next.js ์ค์น ์ฌ๋ถ ์ง๋ฌธ
|
|
26
24
|
inquirer
|
|
@@ -105,19 +103,6 @@ inquirer
|
|
|
105
103
|
copyFileSync(tokenFile, targetTokenFile);
|
|
106
104
|
console.log(`โ
Copied: token.json -> ${targetTokenFile}`);
|
|
107
105
|
|
|
108
|
-
if (!fs.existsSync(targetTokensDir))
|
|
109
|
-
mkdirSync(targetTokensDir, { recursive: true });
|
|
110
|
-
|
|
111
|
-
const tokenFiles = readdirSync(tokenDir).filter((file) =>
|
|
112
|
-
file.endsWith(".js")
|
|
113
|
-
);
|
|
114
|
-
tokenFiles.forEach((file) => {
|
|
115
|
-
const sourcePath = path.join(tokenDir, file);
|
|
116
|
-
const targetPath = path.join(targetTokensDir, file);
|
|
117
|
-
copyFileSync(sourcePath, targetPath);
|
|
118
|
-
console.log(`โ
Copied: ${file} -> ${targetPath}`);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
106
|
console.log("๐ Token setup complete!");
|
|
122
107
|
})
|
|
123
108
|
.catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,51 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "atomsystem",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"atomsystem-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"build
|
|
19
|
-
"start": "next start",
|
|
20
|
-
"lint": "next lint"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"react
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"@
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "atomsystem",
|
|
3
|
+
"version": "0.1.19",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"homepage": "https://system.atomground.com",
|
|
6
|
+
"bin": {
|
|
7
|
+
"atomsystem-init": "cli/init.js",
|
|
8
|
+
"atomsystem-add": "cli/add.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"token.json",
|
|
12
|
+
"tokens",
|
|
13
|
+
"tailwind.config.ts",
|
|
14
|
+
"app/templates"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "node build/build-tokens.js && next dev",
|
|
18
|
+
"build": "next build",
|
|
19
|
+
"start": "next start",
|
|
20
|
+
"lint": "next lint",
|
|
21
|
+
"token-build": "node build/build-tokens.js"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@tokens-studio/sd-transforms": "^1.2.8",
|
|
25
|
+
"react": "^18.0.0",
|
|
26
|
+
"react-dom": "^18.0.0",
|
|
27
|
+
"sd-tailwindcss-transformer": "^2.0.0",
|
|
28
|
+
"style-dictionary": "^4.2.0"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@gsap/react": "^2.1.2",
|
|
32
|
+
"@next/third-parties": "^15.0.3",
|
|
33
|
+
"@tailwindcss/postcss": "^4.0.17",
|
|
34
|
+
"@tokens-studio/sd-transforms": "^1.2.8",
|
|
35
|
+
"clsx": "^2.1.1",
|
|
36
|
+
"gsap": "^3.12.7",
|
|
37
|
+
"highlight.js": "^11.11.1",
|
|
38
|
+
"inquirer": "^12.4.2",
|
|
39
|
+
"next": "^15.2.4",
|
|
40
|
+
"react": "^18.0.0",
|
|
41
|
+
"react-dom": "^18.0.0",
|
|
42
|
+
"sd-tailwindcss-transformer": "^2.0.0",
|
|
43
|
+
"style-dictionary": "^4.2.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^20",
|
|
47
|
+
"@types/react": "^18",
|
|
48
|
+
"@types/react-dom": "^18",
|
|
49
|
+
"@vitest/browser": "^3.1.1",
|
|
50
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
51
|
+
"eslint": "^8",
|
|
52
|
+
"eslint-config-next": "15.0.3",
|
|
53
|
+
"playwright": "^1.51.1",
|
|
54
|
+
"postcss": "^8.5.3",
|
|
55
|
+
"tailwindcss": "^4.0.17",
|
|
56
|
+
"typescript": "^5",
|
|
57
|
+
"vitest": "^3.1.1"
|
|
58
|
+
}
|
|
59
|
+
}
|