atomsystem 0.1.47 → 0.1.49

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.
Files changed (62) hide show
  1. package/README.md +153 -55
  2. package/atom.design.md +155 -0
  3. package/atom.uxPattern.md +431 -0
  4. package/components/ui/Badge.tsx +20 -0
  5. package/components/ui/Button.tsx +33 -0
  6. package/components/ui/Card.tsx +20 -0
  7. package/components/ui/Input.tsx +35 -0
  8. package/components/ui/Modal.tsx +54 -0
  9. package/components/ui/index.ts +5 -0
  10. package/css/atom-ui.css +216 -0
  11. package/dist/cli.cjs +105 -0
  12. package/dist/cli.cjs.map +1 -0
  13. package/dist/cli.d.cts +1 -0
  14. package/dist/cli.d.ts +1 -0
  15. package/dist/cli.js +97 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/index.cjs +7897 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +218 -0
  20. package/dist/index.d.ts +218 -0
  21. package/dist/index.js +7845 -447
  22. package/dist/index.js.map +1 -0
  23. package/package.json +120 -81
  24. package/presets/default.design.md +155 -0
  25. package/presets/uxPattern.md +431 -0
  26. package/skills/atomsystem.skill.md +104 -0
  27. package/tokens/token.json +319 -0
  28. package/tokens/tokens.json +318 -976
  29. package/app/templates/Animations/Animation.tsx +0 -282
  30. package/app/templates/Badge/Badge.tsx +0 -28
  31. package/app/templates/Badge/badge.css +0 -28
  32. package/app/templates/Button/Button.tsx +0 -31
  33. package/app/templates/Button/button.css +0 -135
  34. package/app/templates/Card/Card.tsx +0 -50
  35. package/app/templates/Card/card.css +0 -17
  36. package/app/templates/CodeBlock/CodeBlock.tsx +0 -68
  37. package/app/templates/Collapse/Collapse.tsx +0 -207
  38. package/app/templates/Collapse/collapse.css +0 -24
  39. package/app/templates/Dropdown/Dropdown.css +0 -112
  40. package/app/templates/Dropdown/Dropdown.tsx +0 -103
  41. package/app/templates/Form/Form.tsx +0 -289
  42. package/app/templates/Form/form.css +0 -115
  43. package/app/templates/Modal/Modal.tsx +0 -71
  44. package/app/templates/Modal/modal.css +0 -98
  45. package/app/templates/Navigation/Navigation.tsx +0 -169
  46. package/app/templates/Navigation/navigation.css +0 -59
  47. package/app/templates/Pagination/Pagination.tsx +0 -104
  48. package/app/templates/Pagination/pagination.css +0 -66
  49. package/app/templates/Sidebar/Sidebar.tsx +0 -139
  50. package/app/templates/Sidebar/sidebar.css +0 -4
  51. package/app/templates/TOC/TOC.tsx +0 -165
  52. package/app/templates/Tab/Tab.tsx +0 -117
  53. package/app/templates/Tab/tab.css +0 -56
  54. package/app/templates/Table/Table.tsx +0 -54
  55. package/app/templates/Table/table.css +0 -58
  56. package/app/templates/Theme/Theme.tsx +0 -121
  57. package/app/templates/Toast/Toast.tsx +0 -106
  58. package/app/templates/Toast/toast.css +0 -69
  59. package/build/build-tokens.js +0 -223
  60. package/build/css/_variables.css +0 -251
  61. package/build/typescript/theme.ts +0 -6
  62. package/css/text.css +0 -18
@@ -1,117 +0,0 @@
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 = "tab",
10
- tabNavItemClass = "tab-nav-item",
11
- tabNavItemActiveClass = "tab-nav-item-active",
12
- tabContents,
13
- tabContentClass = "tab-content",
14
- tabContentItem = "tab-content-item",
15
- expectionValue,
16
- expectionLayout,
17
- }: {
18
- /**탭 네비게이션을 정의내립니다.
19
- */
20
- tabs: Array<{
21
- /** id는 탭 메뉴의 고유 값입니다. 고유 값을 통해 Active 상태 값을 관리합니다.*/
22
- id: string;
23
- /** target은 배열로 제공되며, 타겟의 배열과 동일한 내용을 tabContent로 적용됩니다.*/
24
- target: string[];
25
- /** 탭 메뉴의 ReactNode 내용을 받습니다.*/
26
- tabItem: React.ReactNode;
27
- }>;
28
- /** 탭 메뉴의 tailwinds css 스타일 지정을 위한 class를 받습니다.*/
29
- tabClass?: string;
30
- /** 탭 메뉴의 각각의 tailwinds css 스타일 지정을 위한 class를 받습니다.*/
31
- tabNavItemClass?: string;
32
- /** 탭 메뉴의 활성화 상태의 tailwinds css 스타일 지정을 위한 class를 받습니다.*/
33
- tabNavItemActiveClass?: string;
34
- /** 탭 메뉴의 콘텐츠 값을 정의내립니다.*/
35
- tabContents: Array<{
36
- /** 고유 ID 값입니다.*/
37
- id: string;
38
- /** 태그 값으로 tabs target에서 선택된 내용만 화면에 제공됩니다.*/
39
- tag: string;
40
- /** 탭 메뉴의 콘텐츠 ReactNode 정보를 받습니다.*/
41
- content: React.ReactNode;
42
- }>;
43
- /** 탭 메뉴의 콘텐츠 레이아웃을 위한한 tailwinds css 스타일 지정을 위한 class를 받습니다. */
44
- tabContentClass?: string;
45
- /** 탭 메뉴의 콘텐츠의 각 개별 요소를 위한 tailwinds css 스타일 지정을 위한 class를 받습니다. */
46
- tabContentItem?: string;
47
- /** 예외 레이아웃을 사용할 콘텐츠의 tag값을 입력받습니다. */
48
- expectionValue?: string;
49
- /** 예외 레이아웃에 사용하기 위한 tailwinds css 스타일 지정을 위한 class를 받습니다. */
50
- expectionLayout?: string;
51
- }) => {
52
- const [activeContent, setActiveContent] = useState(tabs[0].target);
53
- const [activeTab, setActiveTab] = useState(tabs[0].id);
54
- const tabContentRef = useRef<HTMLDivElement>(null);
55
-
56
- const handleTabActive = (target: string[], id: string) => {
57
- setActiveContent(target);
58
- setActiveTab(id);
59
- };
60
-
61
- // 👇 activeContent가 변경된 이후에 실행
62
- useGSAP(() => {
63
- if (tabContentRef.current) {
64
- gsap.fromTo(
65
- tabContentRef.current.children,
66
- { y: 50, opacity: 0 },
67
- {
68
- y: 0,
69
- opacity: 1,
70
- stagger: 0.1,
71
- duration: 0.6,
72
- ease: "power4.out",
73
- }
74
- );
75
- }
76
- }, [activeContent]);
77
-
78
- return (
79
- <div className="flex flex-col w-full">
80
- <div className={tabClass}>
81
- {tabs.map((tab) => {
82
- return (
83
- <button
84
- key={tab.id}
85
- id={tab.id}
86
- onClick={() => handleTabActive(tab.target, tab.id)}
87
- className={`${tabNavItemClass} ${
88
- activeTab == tab.id
89
- ? tabNavItemActiveClass
90
- : ""
91
- }`}
92
- >
93
- {tab.tabItem}
94
- </button>
95
- );
96
- })}
97
- </div>
98
- <div ref={tabContentRef} className={tabContentClass}>
99
- {tabContents.map((tabContent) => {
100
- return activeContent.includes(tabContent.tag) ? (
101
- <div
102
- key={tabContent.id}
103
- id={tabContent.tag}
104
- className={`${tabContentItem} ${
105
- activeTab == expectionValue ? expectionLayout : ""
106
- }`}
107
- >
108
- {tabContent.content}
109
- </div>
110
- ) : null;
111
- })}
112
- </div>
113
- </div>
114
- );
115
- };
116
-
117
- export default Tab;
@@ -1,56 +0,0 @@
1
- .tab {
2
- display: flex;
3
- width: 100%;
4
- background-color: var(--color-bg-wrapper);
5
- border-bottom: 1px solid var(--color-border-standard);
6
- position: relative;
7
- z-index: 0;
8
- height: 57px;
9
- }
10
-
11
- .tab-nav-item {
12
- padding: var(--spacing-common-x);
13
- cursor: pointer;
14
- border: 1px solid transparent; /* reserve space for active state border */
15
- border-bottom: none;
16
- background-color: transparent;
17
- color: var(--color-text-standard);
18
- transition: all 0.2s ease-in-out;
19
- display: flex;
20
- align-items: center;
21
- justify-content: center;
22
- }
23
-
24
- .tab-nav-item:hover {
25
- background-color: var(--color-bg-card);
26
- }
27
-
28
- .tab-nav-item-active {
29
- background-color: var(--color-bg-card);
30
- border: 1px solid var(--color-border-standard);
31
- border-bottom: 1px solid var(--color-bg-card);
32
- position: relative;
33
- z-index: 10;
34
- color: var(--color-primary);
35
- }
36
-
37
- .tab-content {
38
- background-color: var(--color-bg-card);
39
- padding: var(--spacing-common-x);
40
- border: 1px solid var(--color-border-standard);
41
- border-top: 0;
42
- margin-top: -1px; /* Overlap the top line of content with the bottom line of tab to allow the active tab to connect */
43
- position: relative;
44
- z-index: 1;
45
- }
46
-
47
- .tab-content-item {
48
- color: var(--color-text-standard);
49
- width: 100%;
50
- }
51
-
52
- @media (min-width: 640px) {
53
- .tab-content-item {
54
- width: auto;
55
- }
56
- }
@@ -1,54 +0,0 @@
1
- /** 테이블에 표시할 데이터 배열. 각 객체는 하나의 행을 나타냅니다.
2
- * 객체의 키는 테이블 헤더가 되고, 값은 해당 셀의 내용이 됩니다.
3
- */
4
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
- const Table = ({ tableData }: { tableData: Record<string, any>[] }) => {
6
- // tableData가 제공되지 않았거나 비어있는 경우, 데이터 없음 메시지를 표시합니다.
7
- if (!tableData || tableData.length === 0) {
8
- return (
9
- <div className="table-empty">
10
- <p>표시할 데이터가 없습니다.</p>
11
- </div>
12
- );
13
- }
14
-
15
- const headers = Object.keys(tableData[0]);
16
-
17
- return (
18
- <div className="table-container">
19
- <table className="table">
20
- <thead className="table-header">
21
- <tr>
22
- {/* 헤더 배열을 순회하며 각 헤더에 대한 <th>태그를 생성합니다. */}
23
- {headers.map((header, index) => {
24
- return (
25
- <th key={index}>
26
- {header}
27
- </th>
28
- );
29
- })}
30
- </tr>
31
- </thead>
32
- {/* 테이블 바디 부분 */}
33
- <tbody className="table-body">
34
- {/* tableData 배열을 순회하며 각 데이터 객체(행)에 대한 <tr> 태그를 생성합니다. */}
35
- {tableData.map((row, rowIndex) => {
36
- return (
37
- <tr key={rowIndex} className="table-row">
38
- {/* 각 행 내에서 헤더 배열을 다시 순회하며 해당 헤더에 맞는 셀(<td>)을 생성합니다. */}
39
- {headers.map((header, colIndex) => (
40
- <td key={colIndex} className="table-cell">
41
- {/* 현재 행 객체(row)에서 해당 헤더(키)에 해당하는 값을 가져와 셀에 표시합니다. */}
42
- {row[header]}
43
- </td>
44
- ))}
45
- </tr>
46
- );
47
- })}
48
- </tbody>
49
- </table>
50
- </div>
51
- );
52
- };
53
-
54
- export default Table;
@@ -1,58 +0,0 @@
1
- .table-container {
2
- overflow: auto;
3
- width: 100%;
4
- }
5
-
6
- .table {
7
- min-width: 100%;
8
- border-collapse: collapse;
9
- border-radius: var(--radius-common);
10
- border: 1px solid var(--color-table-header-border);
11
- overflow: hidden;
12
- }
13
-
14
- .table-header {
15
- background-color: var(--color-table-header-bg);
16
- }
17
-
18
- .table-header th {
19
- padding: var(--spacing-common-y) var(--spacing-common-x);
20
- text-align: center;
21
- color: var(--color-table-header-text);
22
- font-weight: var(--font-weight-header);
23
- font-size: var(--text-body);
24
- letter-spacing: 0.05em;
25
- border-bottom: 1px solid var(--color-table-header-border);
26
- }
27
-
28
- .table-body {
29
- /* Body styles if needed */
30
- }
31
-
32
- .table-row {
33
- background-color: var(--color-table-body-bg);
34
- transition: background-color 0.2s ease;
35
- }
36
-
37
- .table-row:nth-child(even) {
38
- background-color: var(--color-table-body-bgEven);
39
- }
40
-
41
- .table-row:hover {
42
- filter: brightness(0.95);
43
- }
44
-
45
- .table-cell {
46
- padding: var(--spacing-sm-y) var(--spacing-common-x);
47
- color: var(--color-table-body-text);
48
- font-size: var(--text-body);
49
- border-bottom: 1px solid var(--color-table-body-border);
50
- text-align: left;
51
- }
52
-
53
- .table-empty {
54
- padding: var(--spacing-common-x);
55
- text-align: center;
56
- color: var(--color-text-standard);
57
- font-size: var(--text-body);
58
- }
@@ -1,121 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useState } from "react";
4
- import { Radio } from "../Form/Form";
5
- import { themes } from "@/build/typescript/theme";
6
- import { Button } from "../Button/Button";
7
- import { ModalContent } from "../Modal/Modal";
8
-
9
- export const ThemeSelector = () => {
10
- const [selectedThemes, setSelectedThemes] = useState<Record<string, string>>({});
11
- const [tempThemes, setTempThemes] = useState<Record<string, string>>({});
12
- const [isThemeLoading, setThemeLoading] = useState(false);
13
- const [isModalOpen, setIsModalOpen] = useState(false);
14
-
15
- useEffect(() => {
16
- const chageThemeValue = () => {
17
- const newThemes: Record<string, string> = {};
18
- Object.entries(themes).forEach(([group, groupThemes]) => {
19
- const storedKey = group === "global" ? "theme" : `theme-${group}`;
20
- const storedTheme = localStorage.getItem(storedKey) || groupThemes[0];
21
- newThemes[group] = storedTheme;
22
- });
23
- setSelectedThemes(newThemes);
24
- setTempThemes(newThemes);
25
- };
26
-
27
- chageThemeValue();
28
- setThemeLoading(true);
29
- }, []);
30
-
31
- const handleTempThemeChange = (group: string, theme: string) => {
32
- setTempThemes((prev) => ({ ...prev, [group]: theme }));
33
- };
34
-
35
- const handleApply = () => {
36
- if (typeof window !== "undefined") {
37
- Object.entries(tempThemes).forEach(([group, theme]) => {
38
- const storedKey = group === "global" ? "theme" : `theme-${group}`;
39
- localStorage.setItem(storedKey, theme);
40
- });
41
- window.dispatchEvent(new Event("storage"));
42
- setSelectedThemes(tempThemes);
43
- setIsModalOpen(false);
44
- }
45
- };
46
-
47
- const handleOpenModal = () => {
48
- setTempThemes(selectedThemes); // Reset to current applied themes
49
- setIsModalOpen(true);
50
- };
51
-
52
- if (isThemeLoading === false) {
53
- return null;
54
- }
55
-
56
- return (
57
- <>
58
- <Button type="primary" onClick={handleOpenModal} style={{ width: "100%" }}>
59
- <div className="flex items-center justify-center gap-2">
60
- <span className="material-symbols-outlined" style={{ fontSize: "18px" }}>palette</span>
61
- <span>테마 설정</span>
62
- </div>
63
- </Button>
64
- <ModalContent
65
- isOpen={isModalOpen}
66
- onRequestClose={() => setIsModalOpen(false)}
67
- modalTitle="테마 설정"
68
- modalFooter={
69
- <div className="flex justify-end gap-2 w-full mt-4">
70
- <Button type="gray" onClick={() => setIsModalOpen(false)}>취소</Button>
71
- <Button type="primary" onClick={handleApply}>적용</Button>
72
- </div>
73
- }
74
- >
75
- <div className="flex flex-col gap-6 p-4">
76
- {Object.entries(themes).map(([group, groupThemes]) => (
77
- <div key={group} className="flex flex-col gap-3">
78
- <p className="font-bold capitalize">{group} Theme</p>
79
- <div className="flex gap-4 flex-wrap">
80
- {groupThemes.map((theme) => (
81
- <Radio
82
- name={`theme-${group}-modal`}
83
- value={theme}
84
- key={theme}
85
- onChange={() => handleTempThemeChange(group, theme)}
86
- checked={theme === tempThemes[group]}
87
- />
88
- ))}
89
- </div>
90
- </div>
91
- ))}
92
- </div>
93
- </ModalContent>
94
- </>
95
- );
96
- };
97
-
98
- export const ThemeProvider = () => {
99
- const [isThemeLoading, setThemeLoading] = useState(false);
100
-
101
- useEffect(() => {
102
- const listenStorageChange = () => {
103
- Object.entries(themes).forEach(([group, groupThemes]) => {
104
- const storedKey = group === "global" ? "theme" : `theme-${group}`;
105
- const currentTheme = localStorage.getItem(storedKey) || groupThemes[0];
106
-
107
- document.documentElement.classList.remove(...groupThemes);
108
- document.documentElement.classList.add(currentTheme);
109
- });
110
- };
111
-
112
- listenStorageChange();
113
- window.addEventListener("storage", listenStorageChange);
114
- setThemeLoading(true);
115
- return () => window.removeEventListener("storage", listenStorageChange);
116
- }, []);
117
-
118
- if (isThemeLoading === false) {
119
- return null;
120
- }
121
- };
@@ -1,106 +0,0 @@
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?: "standard" | "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "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 = Math.random().toString(36).substring(2) + Date.now().toString(36);
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="toast-container">
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={`toast-item ${message.status || "standard"}`}
101
- >
102
- <p>{message.message}</p>
103
- {message.desc ? <p className="toast-desc">{message.desc}</p> : null}
104
- </div>
105
- );
106
- };
@@ -1,69 +0,0 @@
1
- .toast-container {
2
- position: fixed;
3
- display: flex;
4
- flex-direction: column;
5
- gap: var(--spacing-sm-y);
6
- bottom: 24px;
7
- right: 24px;
8
- z-index: 50;
9
- }
10
-
11
- .toast-item {
12
- background-color: var(--toast-bg, var(--color-bg-card));
13
- border: 1px solid var(--toast-border, var(--border-standard));
14
- border-radius: var(--radius-common);
15
- color: var(--toast-color, var(--text-standard));
16
- padding: var(--spacing-common-x);
17
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
18
- min-width: 250px;
19
- }
20
-
21
- /* Status Variants */
22
- .toast-item.standard {
23
- --toast-bg: var(--color-bg-card);
24
- --toast-border: var(--border-standard);
25
- --toast-color: var(--text-standard);
26
- }
27
-
28
- .toast-item.primary {
29
- --toast-bg: var(--color-primary);
30
- --toast-border: var(--color-primary);
31
- --toast-color: var(--color-white);
32
- }
33
-
34
- .toast-item.secondary {
35
- --toast-bg: var(--color-secondary);
36
- --toast-border: var(--color-secondary);
37
- --toast-color: var(--color-white);
38
- }
39
-
40
- .toast-item.success {
41
- --toast-bg: var(--color-success);
42
- --toast-border: var(--color-success);
43
- --toast-color: var(--color-white);
44
- }
45
-
46
- .toast-item.danger,
47
- .toast-item.error {
48
- --toast-bg: var(--color-danger);
49
- --toast-border: var(--color-danger);
50
- --toast-color: var(--color-white);
51
- }
52
-
53
- .toast-item.warning {
54
- --toast-bg: var(--color-warning);
55
- --toast-border: var(--color-warning);
56
- --toast-color: var(--color-white);
57
- }
58
-
59
- .toast-item.info {
60
- --toast-bg: var(--color-info);
61
- --toast-border: var(--color-info);
62
- --toast-color: var(--color-white);
63
- }
64
-
65
- .toast-desc {
66
- font-size: var(--text-sm);
67
- margin-top: var(--spacing-gap-small);
68
- opacity: 0.9;
69
- }