atomsystem 0.1.46 → 0.1.48
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/Checkbox/Checkbox.tsx +55 -0
- package/app/templates/Checkbox/checkbox.css +38 -0
- package/app/templates/Input/Input.tsx +29 -0
- package/app/templates/Input/input.css +24 -0
- package/app/templates/Label/Label.tsx +22 -0
- package/app/templates/Radio/Radio.tsx +45 -0
- package/app/templates/Radio/radio.css +29 -0
- package/app/templates/{Form/Form.tsx → Select/Select.tsx} +143 -289
- package/app/templates/Select/select.css +47 -0
- package/app/templates/Theme/Theme.tsx +2 -1
- package/dist/index.js +216 -43
- package/package.json +3 -3
- package/app/templates/Form/form.css +0 -115
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import "./checkbox.css";
|
|
4
|
+
|
|
5
|
+
export const Checkbox = ({
|
|
6
|
+
text,
|
|
7
|
+
onChange,
|
|
8
|
+
disabled,
|
|
9
|
+
required,
|
|
10
|
+
defaultChecked,
|
|
11
|
+
}: {
|
|
12
|
+
text?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
onChange?: () => void;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
defaultChecked?: boolean;
|
|
18
|
+
}) => {
|
|
19
|
+
return (
|
|
20
|
+
<label className="checkbox-container gap-1.5">
|
|
21
|
+
<input
|
|
22
|
+
type="checkbox"
|
|
23
|
+
className="appearance-none hidden"
|
|
24
|
+
onChange={onChange}
|
|
25
|
+
disabled={disabled}
|
|
26
|
+
required={required}
|
|
27
|
+
defaultChecked={defaultChecked}
|
|
28
|
+
/>
|
|
29
|
+
<span className="checkbox-box">
|
|
30
|
+
<svg
|
|
31
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
32
|
+
className="h-4 w-4"
|
|
33
|
+
viewBox="0 0 20 20"
|
|
34
|
+
strokeWidth="1"
|
|
35
|
+
>
|
|
36
|
+
<path
|
|
37
|
+
fillRule="evenodd"
|
|
38
|
+
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
39
|
+
clipRule="evenodd"
|
|
40
|
+
></path>
|
|
41
|
+
</svg>
|
|
42
|
+
</span>
|
|
43
|
+
{text ? (
|
|
44
|
+
<span
|
|
45
|
+
className={clsx(
|
|
46
|
+
"text-text-standard text-nowrap select-none",
|
|
47
|
+
{ "!text-text-disabled": disabled }
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
{text}
|
|
51
|
+
</span>
|
|
52
|
+
) : null}
|
|
53
|
+
</label>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
.checkbox-container{
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
position: relative;
|
|
5
|
+
cursor: pointer;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.checkbox-box {
|
|
9
|
+
width: var(--spacing-small);
|
|
10
|
+
height: var(--spacing-small);
|
|
11
|
+
border: 1px solid var(--color-border-standard);
|
|
12
|
+
border-radius: var(--radius-common);
|
|
13
|
+
background: var(--color-bg-input-standard);
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
transition: all 0.2s;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.checkbox-box svg {
|
|
21
|
+
color: transparent;
|
|
22
|
+
fill: transparent;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
input:checked + .checkbox-box {
|
|
26
|
+
background: var(--color-primary);
|
|
27
|
+
border-color: var(--color-primary);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
input:checked + .checkbox-box svg {
|
|
31
|
+
color: var(--color-text-active);
|
|
32
|
+
fill: var(--color-text-active);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
input:disabled + .checkbox-box {
|
|
36
|
+
background: var(--color-bg-input-disabled);
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "./input.css";
|
|
4
|
+
|
|
5
|
+
export const Input = ({
|
|
6
|
+
type = "text",
|
|
7
|
+
placeholder = "값을 입력해주세요",
|
|
8
|
+
readOnly,
|
|
9
|
+
disabled,
|
|
10
|
+
required,
|
|
11
|
+
}: {
|
|
12
|
+
type?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
readOnly?: boolean;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
}) => {
|
|
19
|
+
return (
|
|
20
|
+
<input
|
|
21
|
+
className="input"
|
|
22
|
+
type={type}
|
|
23
|
+
placeholder={placeholder}
|
|
24
|
+
readOnly={readOnly}
|
|
25
|
+
disabled={disabled}
|
|
26
|
+
required={required}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.input{
|
|
2
|
+
background: var(--color-bg-input-standard);
|
|
3
|
+
border: 1px solid var(--color-border-standard);
|
|
4
|
+
color: var(--color-text-standard);
|
|
5
|
+
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
6
|
+
border-radius: var(--radius-common);
|
|
7
|
+
outline: none;
|
|
8
|
+
width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.input::placeholder{
|
|
12
|
+
color: var(--color-text-placeholder);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.input:focus{
|
|
16
|
+
border: 1px solid var(--color-border-active);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.input:disabled{
|
|
20
|
+
background: var(--color-bg-input-disabled);
|
|
21
|
+
border: 1px solid var(--color-border-standard);
|
|
22
|
+
color: var(--color-text-disabled);
|
|
23
|
+
cursor: not-allowed;
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
|
|
4
|
+
export const Label = ({
|
|
5
|
+
label,
|
|
6
|
+
required,
|
|
7
|
+
}: {
|
|
8
|
+
label: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
}) => {
|
|
11
|
+
return (
|
|
12
|
+
<p className={"mb-1 text-text-standard"}>
|
|
13
|
+
<span
|
|
14
|
+
className={clsx("", {
|
|
15
|
+
"after:content-['*'] after:ml-1 after:text-danger": required,
|
|
16
|
+
})}
|
|
17
|
+
>
|
|
18
|
+
{label}
|
|
19
|
+
</span>
|
|
20
|
+
</p>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import "./radio.css";
|
|
4
|
+
|
|
5
|
+
export const Radio = ({
|
|
6
|
+
name,
|
|
7
|
+
value,
|
|
8
|
+
label,
|
|
9
|
+
className,
|
|
10
|
+
onChange,
|
|
11
|
+
defaultChecked,
|
|
12
|
+
checked,
|
|
13
|
+
}: {
|
|
14
|
+
name: string;
|
|
15
|
+
value: string;
|
|
16
|
+
label?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
onChange?: () => void;
|
|
19
|
+
defaultChecked?: boolean;
|
|
20
|
+
checked?: boolean;
|
|
21
|
+
}) => {
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
{label ? <p>{label}</p> : null}
|
|
25
|
+
<label className="flex items-center gap-1 cursor-pointer">
|
|
26
|
+
<input
|
|
27
|
+
type="radio"
|
|
28
|
+
className={clsx(
|
|
29
|
+
"relative appearance-none radio rounded-full ",
|
|
30
|
+
{
|
|
31
|
+
"checked:bg-primary after:w-2.5 after:h-2.5 after:bg-[var(--color-bg-input-standard)] after:content-[''] after:absolute after:rounded-full after:left-1 after:top-1":
|
|
32
|
+
true,
|
|
33
|
+
},
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
36
|
+
name={name}
|
|
37
|
+
onChange={onChange}
|
|
38
|
+
defaultChecked={defaultChecked}
|
|
39
|
+
checked={checked}
|
|
40
|
+
/>
|
|
41
|
+
<span>{value}</span>
|
|
42
|
+
</label>
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
.radio{
|
|
2
|
+
position: relative;
|
|
3
|
+
width: var(--spacing-small);
|
|
4
|
+
height: var(--spacing-small);
|
|
5
|
+
border: 1px solid var(--color-border-standard);
|
|
6
|
+
background: var(--color-bg-input-standard);
|
|
7
|
+
color: var(--color-bg-input-standard);
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.radio::after{
|
|
12
|
+
content:'';
|
|
13
|
+
position:absolute;
|
|
14
|
+
left: 50%;
|
|
15
|
+
top: 50%;
|
|
16
|
+
transform: translate(-50%, -50%);
|
|
17
|
+
width: var(--spacing-common-y);
|
|
18
|
+
height: var(--spacing-common-y);
|
|
19
|
+
border-radius: var(--radius-circle);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.radio:checked::after{
|
|
23
|
+
background: var(--color-primary);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.radio:disabled {
|
|
27
|
+
background: var(--color-bg-input-disabled);
|
|
28
|
+
cursor: not-allowed;
|
|
29
|
+
}
|
|
@@ -1,289 +1,143 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
export const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<span className="text-base input-text-value">arrow_drop_down</span>
|
|
145
|
-
</span>
|
|
146
|
-
</div>
|
|
147
|
-
</label>
|
|
148
|
-
{isOpen &&
|
|
149
|
-
createPortal(
|
|
150
|
-
<ul
|
|
151
|
-
ref={dropdownRef}
|
|
152
|
-
className="absolute w-full select-menu"
|
|
153
|
-
style={{
|
|
154
|
-
top: menuPosition.top,
|
|
155
|
-
left: menuPosition.left,
|
|
156
|
-
width: menuPosition.width,
|
|
157
|
-
}}
|
|
158
|
-
>
|
|
159
|
-
{filteredOptions.length > 0 ? (
|
|
160
|
-
filteredOptions.map((option, index) => (
|
|
161
|
-
<li
|
|
162
|
-
key={index}
|
|
163
|
-
className="select-menu-item"
|
|
164
|
-
onClick={() => {
|
|
165
|
-
setFilterValue(option);
|
|
166
|
-
setIsOpen(false);
|
|
167
|
-
}}
|
|
168
|
-
>
|
|
169
|
-
{option}
|
|
170
|
-
</li>
|
|
171
|
-
))
|
|
172
|
-
) : (
|
|
173
|
-
<li className="select-menu-item">No results</li>
|
|
174
|
-
)}
|
|
175
|
-
</ul>,
|
|
176
|
-
document.body
|
|
177
|
-
)}
|
|
178
|
-
<select
|
|
179
|
-
className="select block sm:hidden"
|
|
180
|
-
>
|
|
181
|
-
{options.map((option, index) => {
|
|
182
|
-
return (
|
|
183
|
-
<option key={index} value={option}>
|
|
184
|
-
{option}
|
|
185
|
-
</option>
|
|
186
|
-
);
|
|
187
|
-
})}
|
|
188
|
-
</select>
|
|
189
|
-
</div>
|
|
190
|
-
);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
export const Radio = ({
|
|
194
|
-
name,
|
|
195
|
-
value,
|
|
196
|
-
label,
|
|
197
|
-
className,
|
|
198
|
-
onChange,
|
|
199
|
-
defaultChecked,
|
|
200
|
-
checked,
|
|
201
|
-
}: {
|
|
202
|
-
name: string;
|
|
203
|
-
value: string;
|
|
204
|
-
label?: string;
|
|
205
|
-
className?: string;
|
|
206
|
-
onChange?: () => void;
|
|
207
|
-
defaultChecked?: boolean;
|
|
208
|
-
checked?: boolean;
|
|
209
|
-
}) => {
|
|
210
|
-
return (
|
|
211
|
-
<>
|
|
212
|
-
{label ? <p>{label}</p> : null}
|
|
213
|
-
<label className="flex items-center gap-1">
|
|
214
|
-
<input
|
|
215
|
-
type="radio"
|
|
216
|
-
className={clsx(
|
|
217
|
-
"relative appearance-none radio rounded-full ",
|
|
218
|
-
{
|
|
219
|
-
"checked:bg-primary after:w-2.5 after:h-2.5 after:bg-[var(--input-background-standard)] after:content-[''] after:absolute after:rounded-full after:left-1 after:top-1":
|
|
220
|
-
true,
|
|
221
|
-
},
|
|
222
|
-
{ "focus:border-input-border-focus": true },
|
|
223
|
-
{ "disabled:bg-input-background-disabled": true },
|
|
224
|
-
className
|
|
225
|
-
)}
|
|
226
|
-
name={name}
|
|
227
|
-
onChange={onChange}
|
|
228
|
-
defaultChecked={defaultChecked}
|
|
229
|
-
checked={checked}
|
|
230
|
-
/>
|
|
231
|
-
<span>{value}</span>
|
|
232
|
-
</label>
|
|
233
|
-
</>
|
|
234
|
-
);
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
export const Checkbox = ({
|
|
238
|
-
text,
|
|
239
|
-
onChange,
|
|
240
|
-
disabled,
|
|
241
|
-
required,
|
|
242
|
-
defaultChecked,
|
|
243
|
-
}: {
|
|
244
|
-
text?: string;
|
|
245
|
-
className?: string;
|
|
246
|
-
onChange?: () => void;
|
|
247
|
-
disabled?: boolean;
|
|
248
|
-
required?: boolean;
|
|
249
|
-
defaultChecked?: boolean;
|
|
250
|
-
}) => {
|
|
251
|
-
return (
|
|
252
|
-
<>
|
|
253
|
-
<label className="checkbox gap-1.5">
|
|
254
|
-
<input
|
|
255
|
-
type="checkbox"
|
|
256
|
-
className="appearance-none hidden"
|
|
257
|
-
onChange={onChange}
|
|
258
|
-
disabled={disabled}
|
|
259
|
-
required={required}
|
|
260
|
-
defaultChecked={defaultChecked}
|
|
261
|
-
/>
|
|
262
|
-
<span>
|
|
263
|
-
<svg
|
|
264
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
265
|
-
className="h-4 w-4"
|
|
266
|
-
viewBox="0 0 20 20"
|
|
267
|
-
strokeWidth="1"
|
|
268
|
-
>
|
|
269
|
-
<path
|
|
270
|
-
fillRule="evenodd"
|
|
271
|
-
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
272
|
-
clipRule="evenodd"
|
|
273
|
-
></path>
|
|
274
|
-
</svg>
|
|
275
|
-
</span>
|
|
276
|
-
{text ? (
|
|
277
|
-
<span
|
|
278
|
-
className={clsx(
|
|
279
|
-
"text-text-standard text-nowrap select-none",
|
|
280
|
-
{ "!text-text-disabled": disabled }
|
|
281
|
-
)}
|
|
282
|
-
>
|
|
283
|
-
{text}
|
|
284
|
-
</span>
|
|
285
|
-
) : null}
|
|
286
|
-
</label>
|
|
287
|
-
</>
|
|
288
|
-
);
|
|
289
|
-
};
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import "./select.css";
|
|
5
|
+
|
|
6
|
+
export const Select = ({
|
|
7
|
+
options = [],
|
|
8
|
+
disabled,
|
|
9
|
+
}: {
|
|
10
|
+
options: string[];
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}) => {
|
|
13
|
+
const [filterValue, setFilterValue] = useState("");
|
|
14
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
15
|
+
const [menuPosition, setMenuPosition] = useState({
|
|
16
|
+
top: 0,
|
|
17
|
+
left: 0,
|
|
18
|
+
width: 0,
|
|
19
|
+
});
|
|
20
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
21
|
+
const dropdownRef = useRef<HTMLUListElement>(null);
|
|
22
|
+
const filteredOptions = options.filter((option) =>
|
|
23
|
+
option.toLowerCase().includes(filterValue.toLowerCase())
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
useLayoutEffect(() => {
|
|
27
|
+
if (isOpen && inputRef.current && dropdownRef.current) {
|
|
28
|
+
const rect = inputRef.current.getBoundingClientRect();
|
|
29
|
+
const dropdownRect = dropdownRef.current.getBoundingClientRect();
|
|
30
|
+
|
|
31
|
+
if (Math.abs(menuPosition.width - rect.width) > 0.5) {
|
|
32
|
+
setMenuPosition({
|
|
33
|
+
top: rect.bottom + window.scrollY,
|
|
34
|
+
left: rect.left,
|
|
35
|
+
width: rect.width,
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let top = rect.bottom + window.scrollY;
|
|
41
|
+
|
|
42
|
+
if (rect.bottom + dropdownRect.height > window.innerHeight) {
|
|
43
|
+
top = rect.top + window.scrollY - dropdownRect.height;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setMenuPosition((prev) => {
|
|
47
|
+
if (
|
|
48
|
+
Math.abs(prev.top - top) < 0.5 &&
|
|
49
|
+
Math.abs(prev.left - rect.left) < 0.5
|
|
50
|
+
) {
|
|
51
|
+
return prev;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
top,
|
|
55
|
+
left: rect.left,
|
|
56
|
+
width: rect.width,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}, [isOpen, filterValue, menuPosition.width]);
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
64
|
+
if (
|
|
65
|
+
dropdownRef.current &&
|
|
66
|
+
!dropdownRef.current.contains(event.target as Node) &&
|
|
67
|
+
inputRef.current &&
|
|
68
|
+
!inputRef.current.contains(event.target as Node)
|
|
69
|
+
) {
|
|
70
|
+
setIsOpen(false);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
74
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
75
|
+
}, []);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className="relative">
|
|
79
|
+
<label className="relative w-fit hidden md:block">
|
|
80
|
+
<input
|
|
81
|
+
ref={inputRef}
|
|
82
|
+
type="text"
|
|
83
|
+
value={filterValue}
|
|
84
|
+
placeholder="값을 선택해주세요..."
|
|
85
|
+
onChange={(e) => {
|
|
86
|
+
setFilterValue(e.target.value);
|
|
87
|
+
}}
|
|
88
|
+
onFocus={() => setIsOpen(true)}
|
|
89
|
+
className="select hidden sm:block"
|
|
90
|
+
disabled={disabled}
|
|
91
|
+
/>
|
|
92
|
+
<div
|
|
93
|
+
className="absolute flex items-center right-0 top-0 h-full px-2"
|
|
94
|
+
>
|
|
95
|
+
<span className="material-symbols-outlined">
|
|
96
|
+
<span className="text-base">arrow_drop_down</span>
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
</label>
|
|
100
|
+
{isOpen &&
|
|
101
|
+
createPortal(
|
|
102
|
+
<ul
|
|
103
|
+
ref={dropdownRef}
|
|
104
|
+
className="absolute w-full select-menu"
|
|
105
|
+
style={{
|
|
106
|
+
top: menuPosition.top,
|
|
107
|
+
left: menuPosition.left,
|
|
108
|
+
width: menuPosition.width,
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
{filteredOptions.length > 0 ? (
|
|
112
|
+
filteredOptions.map((option, index) => (
|
|
113
|
+
<li
|
|
114
|
+
key={index}
|
|
115
|
+
className="select-menu-item"
|
|
116
|
+
onClick={() => {
|
|
117
|
+
setFilterValue(option);
|
|
118
|
+
setIsOpen(false);
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
{option}
|
|
122
|
+
</li>
|
|
123
|
+
))
|
|
124
|
+
) : (
|
|
125
|
+
<li className="select-menu-item">No results</li>
|
|
126
|
+
)}
|
|
127
|
+
</ul>,
|
|
128
|
+
document.body
|
|
129
|
+
)}
|
|
130
|
+
<select
|
|
131
|
+
className="select block sm:hidden"
|
|
132
|
+
>
|
|
133
|
+
{options.map((option, index) => {
|
|
134
|
+
return (
|
|
135
|
+
<option key={index} value={option}>
|
|
136
|
+
{option}
|
|
137
|
+
</option>
|
|
138
|
+
);
|
|
139
|
+
})}
|
|
140
|
+
</select>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.select{
|
|
2
|
+
background: var(--color-bg-input-standard);
|
|
3
|
+
border: 1px solid var(--color-border-standard);
|
|
4
|
+
color: var(--color-text-standard);
|
|
5
|
+
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
6
|
+
border-radius: var(--radius-common);
|
|
7
|
+
width: 100%;
|
|
8
|
+
outline: none;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
}
|
|
11
|
+
.select:focus{
|
|
12
|
+
border: 1px solid var(--color-border-active);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.select:disabled{
|
|
16
|
+
background: var(--color-bg-input-disabled);
|
|
17
|
+
border: 1px solid var(--color-border-standard);
|
|
18
|
+
color: var(--color-text-disabled);
|
|
19
|
+
cursor: not-allowed;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.select-menu {
|
|
23
|
+
border: 1px solid var(--color-border-active);
|
|
24
|
+
border-radius: var(--radius-common);
|
|
25
|
+
overflow:hidden;
|
|
26
|
+
z-index: 9999;
|
|
27
|
+
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
28
|
+
background: var(--color-bg-input-standard);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.select-menu .select-menu-item {
|
|
32
|
+
background: var(--color-bg-input-standard);
|
|
33
|
+
color: var(--color-text-standard);
|
|
34
|
+
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
35
|
+
cursor:pointer;
|
|
36
|
+
list-style: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.select-menu .select-menu-item:hover {
|
|
40
|
+
background: var(--color-bg-input-hover);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.select-menu .select-menu-item:active,
|
|
44
|
+
.select-menu .select-menu-item:focus {
|
|
45
|
+
background: var(--color-primary);
|
|
46
|
+
color: var(--color-text-active);
|
|
47
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
|
-
import { Radio } from "../
|
|
4
|
+
import { Radio } from "../Radio/Radio";
|
|
5
|
+
|
|
5
6
|
import { themes } from "@/build/typescript/theme";
|
|
6
7
|
import { Button } from "../Button/Button";
|
|
7
8
|
import { ModalContent } from "../Modal/Modal";
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,186 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
var init_esm_shims = __esm({
|
|
16
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// src/utils/logger.ts
|
|
22
|
+
import chalk from "chalk";
|
|
23
|
+
var logger;
|
|
24
|
+
var init_logger = __esm({
|
|
25
|
+
"src/utils/logger.ts"() {
|
|
26
|
+
"use strict";
|
|
27
|
+
init_esm_shims();
|
|
28
|
+
logger = {
|
|
29
|
+
error(...args) {
|
|
30
|
+
console.error(chalk.red(...args));
|
|
31
|
+
},
|
|
32
|
+
warn(...args) {
|
|
33
|
+
console.warn(chalk.yellow(...args));
|
|
34
|
+
},
|
|
35
|
+
info(...args) {
|
|
36
|
+
console.info(chalk.cyan(...args));
|
|
37
|
+
},
|
|
38
|
+
success(...args) {
|
|
39
|
+
console.log(chalk.green(...args));
|
|
40
|
+
},
|
|
41
|
+
log(...args) {
|
|
42
|
+
console.log(...args);
|
|
43
|
+
},
|
|
44
|
+
break() {
|
|
45
|
+
console.log("");
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// src/utils/component.ts
|
|
52
|
+
var component_exports = {};
|
|
53
|
+
__export(component_exports, {
|
|
54
|
+
addComponent: () => addComponent,
|
|
55
|
+
baseDir: () => baseDir,
|
|
56
|
+
components: () => components,
|
|
57
|
+
dependenciesMap: () => dependenciesMap,
|
|
58
|
+
templateDir: () => templateDir,
|
|
59
|
+
updateGlobalsCss: () => updateGlobalsCss
|
|
60
|
+
});
|
|
61
|
+
import fs from "fs";
|
|
62
|
+
import path2 from "path";
|
|
63
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
64
|
+
function getBaseDir() {
|
|
65
|
+
const isBuilt = path2.basename(__dirname2) === "dist" || fs.existsSync(path2.join(__dirname2, "index.js"));
|
|
66
|
+
if (isBuilt) {
|
|
67
|
+
return path2.join(__dirname2, "..");
|
|
68
|
+
}
|
|
69
|
+
if (path2.basename(__dirname2) === "utils" || path2.basename(__dirname2) === "commands") {
|
|
70
|
+
return path2.join(__dirname2, "..", "..");
|
|
71
|
+
}
|
|
72
|
+
return path2.join(__dirname2, "..", "..");
|
|
73
|
+
}
|
|
74
|
+
function addComponent(comp, cwd, addedComponents) {
|
|
75
|
+
if (addedComponents.has(comp)) return;
|
|
76
|
+
addedComponents.add(comp);
|
|
77
|
+
const componentSourceDir = path2.join(templateDir, comp);
|
|
78
|
+
const componentPath = path2.join(componentSourceDir, `${comp}.tsx`);
|
|
79
|
+
const targetDir = path2.join(cwd, "app", "components", comp);
|
|
80
|
+
const targetPath = path2.join(targetDir, `${comp}.tsx`);
|
|
81
|
+
const cssFileName = `${comp}.css`.toLowerCase();
|
|
82
|
+
const sourceCssPath = path2.join(componentSourceDir, cssFileName);
|
|
83
|
+
const targetCssPath = path2.join(targetDir, cssFileName);
|
|
84
|
+
if (fs.existsSync(componentPath)) {
|
|
85
|
+
if (!fs.existsSync(targetDir)) {
|
|
86
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
87
|
+
logger.info(`\uB514\uB809\uD1A0\uB9AC \uC0DD\uC131: ${targetDir}`);
|
|
88
|
+
}
|
|
89
|
+
fs.copyFileSync(componentPath, targetPath);
|
|
90
|
+
logger.success(`\u2705 ${comp} \uCEF4\uD3EC\uB10C\uD2B8(TSX)\uAC00 \uCD94\uAC00\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${targetPath}`);
|
|
91
|
+
if (fs.existsSync(sourceCssPath)) {
|
|
92
|
+
fs.copyFileSync(sourceCssPath, targetCssPath);
|
|
93
|
+
logger.success(`\u2705 ${comp} \uCEF4\uD3EC\uB10C\uD2B8(CSS)\uAC00 \uCD94\uAC00\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${targetCssPath}`);
|
|
94
|
+
updateGlobalsCss(comp, cssFileName, cwd);
|
|
95
|
+
}
|
|
96
|
+
if (dependenciesMap[comp]) {
|
|
97
|
+
logger.info(`\u23F3 ${comp}\uC758 \uC758\uC874\uC131 \uCEF4\uD3EC\uB10C\uD2B8 \uD655\uC778 \uC911...`);
|
|
98
|
+
for (const dep of dependenciesMap[comp]) {
|
|
99
|
+
addComponent(dep, cwd, addedComponents);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
logger.error(`\u274C ${comp} \uCEF4\uD3EC\uB10C\uD2B8\uAC00 atomsystem\uC5D0 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${comp}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function updateGlobalsCss(comp, cssFileName, cwd) {
|
|
107
|
+
const globalsPath = path2.join(cwd, "app", "globals.css");
|
|
108
|
+
const importLine = `@import "./components/${comp}/${cssFileName}";`;
|
|
109
|
+
const componentSectionMarker = "/* Component Styles */";
|
|
110
|
+
if (fs.existsSync(globalsPath)) {
|
|
111
|
+
let content = fs.readFileSync(globalsPath, "utf8");
|
|
112
|
+
if (!content.includes(importLine)) {
|
|
113
|
+
if (content.includes(componentSectionMarker)) {
|
|
114
|
+
const markerIndex = content.indexOf(componentSectionMarker);
|
|
115
|
+
const afterMarker = content.slice(markerIndex);
|
|
116
|
+
const lastImportIndex = afterMarker.lastIndexOf("@import");
|
|
117
|
+
if (lastImportIndex !== -1) {
|
|
118
|
+
const endOfLastImport = markerIndex + lastImportIndex + afterMarker.slice(lastImportIndex).indexOf("\n") + 1;
|
|
119
|
+
content = content.slice(0, endOfLastImport) + importLine + "\n" + content.slice(endOfLastImport);
|
|
120
|
+
} else {
|
|
121
|
+
content = content.slice(0, markerIndex + componentSectionMarker.length) + "\n" + importLine + "\n" + content.slice(markerIndex + componentSectionMarker.length);
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
content = content.trimEnd() + "\n\n" + componentSectionMarker + "\n" + importLine + "\n";
|
|
125
|
+
}
|
|
126
|
+
fs.writeFileSync(globalsPath, content, "utf8");
|
|
127
|
+
logger.success(`\u2705 globals.css\uC5D0 ${comp} @import\uB97C \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`);
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
logger.warn(`\u26A0\uFE0F globals.css\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC218\uB3D9\uC73C\uB85C \uCD94\uAC00\uD574 \uC8FC\uC138\uC694:
|
|
131
|
+
${importLine}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
var __filename2, __dirname2, baseDir, templateDir, components, dependenciesMap;
|
|
135
|
+
var init_component = __esm({
|
|
136
|
+
"src/utils/component.ts"() {
|
|
137
|
+
"use strict";
|
|
138
|
+
init_esm_shims();
|
|
139
|
+
init_logger();
|
|
140
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
141
|
+
__dirname2 = path2.dirname(__filename2);
|
|
142
|
+
baseDir = getBaseDir();
|
|
143
|
+
templateDir = path2.join(baseDir, "app", "templates");
|
|
144
|
+
logger.info(`\u{1F50D} Debug: baseDir=${baseDir}`);
|
|
145
|
+
logger.info(`\u{1F50D} Debug: templateDir=${templateDir}`);
|
|
146
|
+
components = [
|
|
147
|
+
"Button",
|
|
148
|
+
"Badge",
|
|
149
|
+
"Card",
|
|
150
|
+
"Collapse",
|
|
151
|
+
"CodeBlock",
|
|
152
|
+
"Navigation",
|
|
153
|
+
"Sidebar",
|
|
154
|
+
"Input",
|
|
155
|
+
"Theme",
|
|
156
|
+
"Tab",
|
|
157
|
+
"Toast",
|
|
158
|
+
"Pagination",
|
|
159
|
+
"Table",
|
|
160
|
+
"Modal",
|
|
161
|
+
"TOC",
|
|
162
|
+
"Dropdown",
|
|
163
|
+
"Checkbox",
|
|
164
|
+
"Radio",
|
|
165
|
+
"Select",
|
|
166
|
+
"Label"
|
|
167
|
+
];
|
|
168
|
+
dependenciesMap = {
|
|
169
|
+
Theme: ["Button", "Radio", "Modal"]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
});
|
|
7
173
|
|
|
8
174
|
// src/index.ts
|
|
175
|
+
init_esm_shims();
|
|
9
176
|
import { Command as Command3 } from "commander";
|
|
10
177
|
|
|
11
178
|
// src/commands/add.ts
|
|
179
|
+
init_esm_shims();
|
|
180
|
+
init_logger();
|
|
181
|
+
init_component();
|
|
12
182
|
import { Command } from "commander";
|
|
13
|
-
import
|
|
183
|
+
import path3 from "path";
|
|
14
184
|
import prompts from "prompts";
|
|
15
185
|
import { z } from "zod";
|
|
16
186
|
var addOptionsSchema = z.object({
|
|
@@ -23,7 +193,7 @@ var add = new Command().name("add").description("\uD504\uB85C\uC81D\uD2B8\uC5D0
|
|
|
23
193
|
const options = addOptionsSchema.parse({
|
|
24
194
|
components: componentsArg,
|
|
25
195
|
...opts,
|
|
26
|
-
cwd:
|
|
196
|
+
cwd: path3.resolve(opts.cwd)
|
|
27
197
|
});
|
|
28
198
|
let selectedComponents = options.components || [];
|
|
29
199
|
if (options.all) {
|
|
@@ -53,17 +223,20 @@ var add = new Command().name("add").description("\uD504\uB85C\uC81D\uD2B8\uC5D0
|
|
|
53
223
|
});
|
|
54
224
|
|
|
55
225
|
// src/commands/init.ts
|
|
226
|
+
init_esm_shims();
|
|
227
|
+
init_logger();
|
|
228
|
+
init_component();
|
|
56
229
|
import { Command as Command2 } from "commander";
|
|
57
|
-
import
|
|
58
|
-
import
|
|
230
|
+
import fs2 from "fs";
|
|
231
|
+
import path4 from "path";
|
|
59
232
|
import { exec } from "child_process";
|
|
60
233
|
import { promisify } from "util";
|
|
61
234
|
import ora from "ora";
|
|
62
235
|
import prompts2 from "prompts";
|
|
63
|
-
import { fileURLToPath } from "url";
|
|
236
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
64
237
|
import { z as z2 } from "zod";
|
|
65
|
-
var
|
|
66
|
-
var
|
|
238
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
239
|
+
var __dirname3 = path4.dirname(__filename3);
|
|
67
240
|
var execPromise = promisify(exec);
|
|
68
241
|
var initOptionsSchema = z2.object({
|
|
69
242
|
cwd: z2.string(),
|
|
@@ -73,11 +246,11 @@ var init = new Command2().name("init").description("\uD504\uB85C\uC81D\uD2B8\uB9
|
|
|
73
246
|
try {
|
|
74
247
|
const options = initOptionsSchema.parse({
|
|
75
248
|
...opts,
|
|
76
|
-
cwd:
|
|
249
|
+
cwd: path4.resolve(opts.cwd)
|
|
77
250
|
});
|
|
78
251
|
const projectRoot = options.cwd;
|
|
79
|
-
const packageJsonPath =
|
|
80
|
-
if (!
|
|
252
|
+
const packageJsonPath = path4.join(projectRoot, "package.json");
|
|
253
|
+
if (!fs2.existsSync(packageJsonPath)) {
|
|
81
254
|
logger.error("\u274C \uC624\uB958: \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
82
255
|
logger.info("'npm init'\uC744 \uBA3C\uC800 \uC2E4\uD589\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.");
|
|
83
256
|
process.exit(1);
|
|
@@ -120,7 +293,7 @@ var init = new Command2().name("init").description("\uD504\uB85C\uC81D\uD2B8\uB9
|
|
|
120
293
|
return;
|
|
121
294
|
}
|
|
122
295
|
try {
|
|
123
|
-
const packageJson = JSON.parse(
|
|
296
|
+
const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
|
|
124
297
|
packageJson.type = "module";
|
|
125
298
|
packageJson.scripts = {
|
|
126
299
|
...packageJson.scripts,
|
|
@@ -130,17 +303,17 @@ var init = new Command2().name("init").description("\uD504\uB85C\uC81D\uD2B8\uB9
|
|
|
130
303
|
"lint": "next lint",
|
|
131
304
|
"token-build": "node build/build-tokens.js"
|
|
132
305
|
};
|
|
133
|
-
|
|
306
|
+
fs2.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
134
307
|
logger.success("\u2705 package.json \uC2A4\uD06C\uB9BD\uD2B8 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC.");
|
|
135
308
|
} catch (err) {
|
|
136
309
|
logger.error("\u274C package.json \uC5C5\uB370\uC774\uD2B8 \uC624\uB958:", err);
|
|
137
310
|
return;
|
|
138
311
|
}
|
|
139
|
-
const appDir =
|
|
140
|
-
if (!
|
|
312
|
+
const appDir = path4.join(projectRoot, "app");
|
|
313
|
+
if (!fs2.existsSync(appDir)) fs2.mkdirSync(appDir, { recursive: true });
|
|
141
314
|
const files = [
|
|
142
315
|
{
|
|
143
|
-
path:
|
|
316
|
+
path: path4.join(appDir, "layout.tsx"),
|
|
144
317
|
content: `import { ThemeProvider } from "./components/Theme/Theme";
|
|
145
318
|
import "./globals.css";
|
|
146
319
|
|
|
@@ -157,13 +330,13 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
157
330
|
}`
|
|
158
331
|
},
|
|
159
332
|
{
|
|
160
|
-
path:
|
|
333
|
+
path: path4.join(appDir, "globals.css"),
|
|
161
334
|
content: `@import "tailwindcss";
|
|
162
335
|
@import "../build/css/_variables.css";
|
|
163
336
|
@import "../css/text.css";`
|
|
164
337
|
},
|
|
165
338
|
{
|
|
166
|
-
path:
|
|
339
|
+
path: path4.join(appDir, "page.tsx"),
|
|
167
340
|
content: `import { ThemeSelector } from "./components/Theme/Theme";
|
|
168
341
|
import { Button } from "./components/Button/Button";
|
|
169
342
|
import { Card, CardHeader, CardContent, CardFooter } from "./components/Card/Card";
|
|
@@ -203,7 +376,7 @@ export default function Page() {
|
|
|
203
376
|
}`
|
|
204
377
|
},
|
|
205
378
|
{
|
|
206
|
-
path:
|
|
379
|
+
path: path4.join(projectRoot, "tsconfig.json"),
|
|
207
380
|
content: JSON.stringify({
|
|
208
381
|
compilerOptions: {
|
|
209
382
|
target: "ES2017",
|
|
@@ -229,25 +402,25 @@ export default function Page() {
|
|
|
229
402
|
}, null, 2)
|
|
230
403
|
},
|
|
231
404
|
{
|
|
232
|
-
path:
|
|
405
|
+
path: path4.join(projectRoot, "next.config.ts"),
|
|
233
406
|
content: `import type { NextConfig } from "next";
|
|
234
407
|
|
|
235
408
|
const nextConfig: NextConfig = { reactStrictMode: false };
|
|
236
409
|
export default nextConfig;`
|
|
237
410
|
},
|
|
238
411
|
{
|
|
239
|
-
path:
|
|
412
|
+
path: path4.join(projectRoot, "postcss.config.mjs"),
|
|
240
413
|
content: `/** @type {import('postcss-load-config').Config} */
|
|
241
414
|
const config = { plugins: { "@tailwindcss/postcss": {} } };
|
|
242
415
|
export default config;`
|
|
243
416
|
}
|
|
244
417
|
];
|
|
245
418
|
for (const file of files) {
|
|
246
|
-
|
|
419
|
+
fs2.writeFileSync(file.path, file.content);
|
|
247
420
|
logger.info(`\u2705 \uD30C\uC77C \uC0DD\uC131: ${file.path}`);
|
|
248
421
|
}
|
|
249
|
-
const buildTokensPath =
|
|
250
|
-
if (
|
|
422
|
+
const buildTokensPath = path4.join(projectRoot, "build", "build-tokens.js");
|
|
423
|
+
if (fs2.existsSync(buildTokensPath)) {
|
|
251
424
|
const spin = ora("\uB514\uC790\uC778 \uD1A0\uD070 \uBE4C\uB4DC \uC911...").start();
|
|
252
425
|
try {
|
|
253
426
|
await execPromise(`node "${buildTokensPath}"`, { cwd: projectRoot });
|
|
@@ -258,14 +431,14 @@ export default config;`
|
|
|
258
431
|
}
|
|
259
432
|
}
|
|
260
433
|
}
|
|
261
|
-
const { baseDir: pkgRoot } = await
|
|
434
|
+
const { baseDir: pkgRoot } = await Promise.resolve().then(() => (init_component(), component_exports));
|
|
262
435
|
logger.info(`\u{1F50D} Debug: pkgRoot=${pkgRoot}`);
|
|
263
|
-
const tokenFile =
|
|
264
|
-
const buildFile =
|
|
265
|
-
const textCssFile =
|
|
266
|
-
copyFile(tokenFile,
|
|
267
|
-
copyFile(buildFile,
|
|
268
|
-
copyFile(textCssFile,
|
|
436
|
+
const tokenFile = path4.join(pkgRoot, "tokens", "tokens.json");
|
|
437
|
+
const buildFile = path4.join(pkgRoot, "build", "build-tokens.js");
|
|
438
|
+
const textCssFile = path4.join(pkgRoot, "css", "text.css");
|
|
439
|
+
copyFile(tokenFile, path4.join(projectRoot, "tokens", "tokens.json"));
|
|
440
|
+
copyFile(buildFile, path4.join(projectRoot, "build", "build-tokens.js"));
|
|
441
|
+
copyFile(textCssFile, path4.join(projectRoot, "css", "text.css"));
|
|
269
442
|
const addedComponents = /* @__PURE__ */ new Set();
|
|
270
443
|
logger.info("\u23F3 \uAE30\uBCF8 \uB370\uBAA8 \uCEF4\uD3EC\uB10C\uD2B8 \uC124\uCE58 \uC911...");
|
|
271
444
|
addComponent("Button", projectRoot, addedComponents);
|
|
@@ -278,10 +451,10 @@ export default config;`
|
|
|
278
451
|
}
|
|
279
452
|
});
|
|
280
453
|
function copyFile(src, dest) {
|
|
281
|
-
const dir =
|
|
282
|
-
if (!
|
|
283
|
-
if (
|
|
284
|
-
|
|
454
|
+
const dir = path4.dirname(dest);
|
|
455
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
456
|
+
if (fs2.existsSync(src)) {
|
|
457
|
+
fs2.copyFileSync(src, dest);
|
|
285
458
|
logger.info(`\u2705 \uBCF5\uC0AC \uC644\uB8CC: ${src} -> ${dest}`);
|
|
286
459
|
} else {
|
|
287
460
|
logger.warn(`\u26A0\uFE0F \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uBCF5\uC0AC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${src}`);
|
|
@@ -291,12 +464,12 @@ function copyFile(src, dest) {
|
|
|
291
464
|
// src/index.ts
|
|
292
465
|
import { readFileSync } from "fs";
|
|
293
466
|
import { join } from "path";
|
|
294
|
-
import { fileURLToPath as
|
|
467
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
295
468
|
import { dirname } from "path";
|
|
296
|
-
var
|
|
297
|
-
var
|
|
469
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
470
|
+
var __dirname4 = dirname(__filename4);
|
|
298
471
|
async function main() {
|
|
299
|
-
const packageJsonPath = join(
|
|
472
|
+
const packageJsonPath = join(__dirname4, "..", "package.json");
|
|
300
473
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
301
474
|
const program = new Command3().name("atomsystem").description("Atom Ground Design System CLI").version(packageJson.version);
|
|
302
475
|
program.addCommand(add);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atomsystem",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"homepage": "https://system.atomground.com",
|
|
6
6
|
"bin": {
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"css"
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
|
-
"dev": "node build/build-tokens.js && next dev",
|
|
18
|
+
"dev": "node build/build-tokens.js && next dev --webpack",
|
|
19
19
|
"build": "next build",
|
|
20
20
|
"start": "next start",
|
|
21
21
|
"lint": "next lint",
|
|
22
22
|
"token-build": "node build/build-tokens.js",
|
|
23
|
-
"cli:build": "tsup src/index.ts --format esm --clean --shims --target node20",
|
|
23
|
+
"cli:build": "tsup src/index.ts --format esm --clean --shims --target node20 --no-splitting",
|
|
24
24
|
"cli:dev": "tsx src/index.ts"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
.input{
|
|
3
|
-
background: var(--bg-input-standard);
|
|
4
|
-
border: 1px solid var(--border-standard);
|
|
5
|
-
color: var(--text-standard);
|
|
6
|
-
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
7
|
-
border-radius: var(--radius-common);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.input::placeholder{
|
|
11
|
-
color: var(--text-placeholder);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.input:focus{
|
|
15
|
-
border: 1px solid var(--border-active);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.input:disabled{
|
|
19
|
-
background: var(--bg-input-disabled);
|
|
20
|
-
border: 1px solid var(--border-standard);
|
|
21
|
-
color: var(--text-disabled);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.select{
|
|
25
|
-
background: var(--bg-input-standard);
|
|
26
|
-
border: 1px solid var(--border-standard);
|
|
27
|
-
color: var(--text-standard);
|
|
28
|
-
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
29
|
-
border-radius: var(--radius-common);
|
|
30
|
-
}
|
|
31
|
-
.select:focus{
|
|
32
|
-
border: 1px solid var(--border-active);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.select:disabled{
|
|
36
|
-
background: var(--bg-input-disabled);
|
|
37
|
-
border: 1px solid var(--border-standard);
|
|
38
|
-
color: var(--text-disabled);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.select-menu {
|
|
42
|
-
border: 1px solid var(--border-active);
|
|
43
|
-
border-radius: var(--radius-common);
|
|
44
|
-
overflow:hidden;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.select-menu .select-menu-item {
|
|
48
|
-
background: var(--bg-input-standard);
|
|
49
|
-
border: 1px solid var(--border-standard);
|
|
50
|
-
color: var(--text-standard);
|
|
51
|
-
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
52
|
-
cursor:pointer
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.select-menu .select-menu-item:hover {
|
|
56
|
-
background: var(--bg-input-hover);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.select-menu .select-menu-item:active,
|
|
60
|
-
.select-menu .select-menu-item:focus {
|
|
61
|
-
background: var(--color-primary);
|
|
62
|
-
border: 1px solid var(--color-primary);
|
|
63
|
-
color: var(--text-active);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.checkbox{
|
|
67
|
-
display: flex;
|
|
68
|
-
align-items: center;
|
|
69
|
-
position: relative;
|
|
70
|
-
width: var(--spacing-small);
|
|
71
|
-
height: var(--spacing-small);
|
|
72
|
-
border: 1px solid var(--border-standard);
|
|
73
|
-
border-radius: var(--radius-common);
|
|
74
|
-
background: var(--bg-input-standard);
|
|
75
|
-
color: var(--bg-input-standard);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.checkbox span svg{
|
|
79
|
-
color: var(--bg-input-standard);
|
|
80
|
-
fill: var(--bg-input-standard);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.checkbox:has(input:checked){
|
|
84
|
-
background: var(--color-primary);
|
|
85
|
-
border: 1px solid var(--color-primary);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.checkbox:has(input:checked) span svg{
|
|
89
|
-
color: var(--text-active);
|
|
90
|
-
fill: var(--text-active);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.radio{
|
|
94
|
-
position: relative;
|
|
95
|
-
width: var(--spacing-small);
|
|
96
|
-
height: var(--spacing-small);
|
|
97
|
-
border: 1px solid var(--border-standard);
|
|
98
|
-
background: var(--bg-input-standard);
|
|
99
|
-
color: var(--bg-input-standard);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.radio::after{
|
|
103
|
-
content:'';
|
|
104
|
-
position:absolute;
|
|
105
|
-
left: 50%;
|
|
106
|
-
top: 50%;
|
|
107
|
-
transform: translate(-50%, -50%);
|
|
108
|
-
width: var(--spacing-common-y);
|
|
109
|
-
height: var(--spacing-common-y);
|
|
110
|
-
border-radius: var(--radius-circle);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.radio:checked::after{
|
|
114
|
-
background: var(--color-primary);
|
|
115
|
-
}
|