podo-ui 0.1.29
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/.next/app-build-manifest.json +16 -0
- package/.next/build-manifest.json +19 -0
- package/.next/cache/.tsbuildinfo +1 -0
- package/.next/cache/config.json +7 -0
- package/.next/cache/webpack/client-development/0.pack.gz +0 -0
- package/.next/cache/webpack/client-development/1.pack.gz +0 -0
- package/.next/cache/webpack/client-development/index.pack.gz +0 -0
- package/.next/cache/webpack/client-development/index.pack.gz.old +0 -0
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-development/0.pack.gz +0 -0
- package/.next/cache/webpack/edge-server-development/index.pack.gz +0 -0
- package/.next/cache/webpack/server-development/0.pack.gz +0 -0
- package/.next/cache/webpack/server-development/index.pack.gz +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/1.pack +0 -0
- package/.next/cache/webpack/server-production/2.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/.next/package.json +1 -0
- package/.next/react-loadable-manifest.json +1 -0
- package/.next/server/app/page.js +5057 -0
- package/.next/server/app/page_client-reference-manifest.js +1 -0
- package/.next/server/app-paths-manifest.json +3 -0
- package/.next/server/edge-runtime-webpack.js +1171 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +28 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages-manifest.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +5 -0
- package/.next/server/static/webpack/633457081244afec._.hot-update.json +1 -0
- package/.next/static/chunks/_app-pages-browser_react_atom_editor_tsx.js +692 -0
- package/.next/static/chunks/app/layout.js +72 -0
- package/.next/static/chunks/app/page.js +233 -0
- package/.next/static/chunks/app-pages-internals.js +127 -0
- package/.next/static/chunks/main-app.js +1875 -0
- package/.next/static/chunks/polyfills.js +1 -0
- package/.next/static/chunks/webpack.js +1427 -0
- package/.next/static/css/_app-pages-browser_react_atom_editor_tsx.css +124 -0
- package/.next/static/css/app/layout.css +20357 -0
- package/.next/static/css/app/page.css +4282 -0
- package/.next/static/development/_buildManifest.js +1 -0
- package/.next/static/development/_ssgManifest.js +1 -0
- package/.next/static/media/Pretendard-Black.4aeaaf12.woff2 +0 -0
- package/.next/static/media/Pretendard-Bold.e00e15f4.woff2 +0 -0
- package/.next/static/media/Pretendard-ExtraBold.dd3e76c5.woff2 +0 -0
- package/.next/static/media/Pretendard-ExtraLight.b6885b7a.woff2 +0 -0
- package/.next/static/media/Pretendard-Light.e725ed3d.woff2 +0 -0
- package/.next/static/media/Pretendard-Medium.12f0acdc.woff2 +0 -0
- package/.next/static/media/Pretendard-Regular.119cf01b.woff2 +0 -0
- package/.next/static/media/Pretendard-SemiBold.cfa29e4f.woff2 +0 -0
- package/.next/static/media/Pretendard-Thin.fc2b22d5.woff2 +0 -0
- package/.next/static/media/icon.e529ac0f.woff +0 -0
- package/.next/static/webpack/633457081244afec._.hot-update.json +1 -0
- package/.next/trace +28 -0
- package/.next/types/app/layout.ts +79 -0
- package/.next/types/app/page.ts +79 -0
- package/.next/types/package.json +1 -0
- package/.prettierignore +9 -0
- package/.prettierrc +8 -0
- package/SECURITY.md +21 -0
- package/cli/icon-scss.js +56 -0
- package/eslint.config.js +26 -0
- package/global.scss +58 -0
- package/index.html +12 -0
- package/mixin.scss +27 -0
- package/next-env.d.ts +5 -0
- package/next.config.mjs +10 -0
- package/next.ts +20 -0
- package/package.json +52 -0
- package/react/atom/editor-view.tsx +12 -0
- package/react/atom/editor.module.scss +134 -0
- package/react/atom/editor.tsx +171 -0
- package/react/atom/input.module.scss +42 -0
- package/react/atom/input.tsx +67 -0
- package/react/atom/textarea.module.scss +17 -0
- package/react/atom/textarea.tsx +51 -0
- package/react/molecule/field.module.scss +22 -0
- package/react/molecule/field.tsx +78 -0
- package/react.ts +16 -0
- package/readme.md +199 -0
- package/scss/button/class.scss +190 -0
- package/scss/button/layout.scss +37 -0
- package/scss/color/class.scss +50 -0
- package/scss/color/function.scss +3 -0
- package/scss/color/theme.scss +177 -0
- package/scss/form/checkbox-radio.scss +148 -0
- package/scss/form/file.scss +67 -0
- package/scss/form/input.scss +189 -0
- package/scss/form/label.scss +25 -0
- package/scss/form/select.scss +57 -0
- package/scss/form/textarea.scss +52 -0
- package/scss/form/toggle.scss +67 -0
- package/scss/icon/font/icon.woff +0 -0
- package/scss/icon/function.scss +5 -0
- package/scss/icon/icon-name.scss +118 -0
- package/scss/icon/icon.scss +38 -0
- package/scss/layout/bg-elevation.scss +52 -0
- package/scss/layout/border.scss +20 -0
- package/scss/layout/device.scss +28 -0
- package/scss/layout/grid.scss +69 -0
- package/scss/layout/hide.scss +23 -0
- package/scss/layout/radius.scss +23 -0
- package/scss/layout/spacing.scss +56 -0
- package/scss/molecule/pagination.scss +0 -0
- package/scss/molecule/tab.scss +46 -0
- package/scss/molecule/table.scss +54 -0
- package/scss/reset.scss +55 -0
- package/scss/typo/font/Pretendard-Black.woff2 +0 -0
- package/scss/typo/font/Pretendard-Bold.woff2 +0 -0
- package/scss/typo/font/Pretendard-ExtraBold.woff2 +0 -0
- package/scss/typo/font/Pretendard-ExtraLight.woff2 +0 -0
- package/scss/typo/font/Pretendard-Light.woff2 +0 -0
- package/scss/typo/font/Pretendard-Medium.woff2 +0 -0
- package/scss/typo/font/Pretendard-Regular.woff2 +0 -0
- package/scss/typo/font/Pretendard-SemiBold.woff2 +0 -0
- package/scss/typo/font/Pretendard-Thin.woff2 +0 -0
- package/scss/typo/font-family.scss +80 -0
- package/scss/typo/font-size.scss +30 -0
- package/scss/typo/mixin.scss +173 -0
- package/scss/typo/typo.scss +102 -0
- package/scss/typo/util.scss +0 -0
- package/src/app/layout.tsx +18 -0
- package/src/app/page.module.scss +71 -0
- package/src/app/page.tsx +2712 -0
- package/src/styles/custom.scss +11 -0
- package/src/styles/font-family.scss +80 -0
- package/src/styles/icon.scss +5 -0
- package/src/vite-env.d.ts +1 -0
- package/system.scss +0 -0
- package/tsconfig.json +26 -0
- package/wrangler.toml +4 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { v4 as uuid } from 'uuid';
|
|
3
|
+
import suneditor from 'suneditor';
|
|
4
|
+
import 'suneditor/dist/css/suneditor.min.css';
|
|
5
|
+
import styles from './editor.module.scss';
|
|
6
|
+
import lang from 'suneditor/src/lang';
|
|
7
|
+
import {
|
|
8
|
+
blockquote,
|
|
9
|
+
align,
|
|
10
|
+
font,
|
|
11
|
+
fontSize,
|
|
12
|
+
fontColor,
|
|
13
|
+
hiliteColor,
|
|
14
|
+
horizontalRule,
|
|
15
|
+
list,
|
|
16
|
+
formatBlock,
|
|
17
|
+
lineHeight,
|
|
18
|
+
template,
|
|
19
|
+
paragraphStyle,
|
|
20
|
+
textStyle,
|
|
21
|
+
link,
|
|
22
|
+
image,
|
|
23
|
+
video,
|
|
24
|
+
table,
|
|
25
|
+
} from 'suneditor/src/plugins';
|
|
26
|
+
import { z } from 'zod';
|
|
27
|
+
|
|
28
|
+
interface Props {
|
|
29
|
+
value: string;
|
|
30
|
+
width?: string;
|
|
31
|
+
height?: string;
|
|
32
|
+
onChange: (content: string) => void;
|
|
33
|
+
imageUploadUrl?: string;
|
|
34
|
+
imageUploadHeader?: Record<string, string>;
|
|
35
|
+
validator?: z.ZodType<unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const SunEditor = ({
|
|
39
|
+
value = '',
|
|
40
|
+
width = '100%',
|
|
41
|
+
height = '600px',
|
|
42
|
+
onChange,
|
|
43
|
+
imageUploadUrl,
|
|
44
|
+
imageUploadHeader,
|
|
45
|
+
validator,
|
|
46
|
+
}: Props) => {
|
|
47
|
+
const [message, setMessage] = useState('');
|
|
48
|
+
const [statusClass, setStatusClass] = useState('');
|
|
49
|
+
const editorRef = useRef<HTMLTextAreaElement>(null);
|
|
50
|
+
const editorID = `f${uuid()}`;
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
const ref = useRef<any>();
|
|
53
|
+
|
|
54
|
+
const validateHandler = (content: string) => {
|
|
55
|
+
setMessage('');
|
|
56
|
+
setStatusClass('');
|
|
57
|
+
if (validator && content.length > 0) {
|
|
58
|
+
try {
|
|
59
|
+
validator.parse(content);
|
|
60
|
+
setStatusClass('success');
|
|
61
|
+
} catch (e) {
|
|
62
|
+
if (e instanceof z.ZodError) {
|
|
63
|
+
setMessage(e.errors[0].message);
|
|
64
|
+
setStatusClass('danger');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const toolbar = [
|
|
70
|
+
['undo', 'redo'],
|
|
71
|
+
['formatBlock', 'bold', 'underline', 'italic', 'strike'],
|
|
72
|
+
['fontColor', 'hiliteColor'],
|
|
73
|
+
['align', 'list', 'table'],
|
|
74
|
+
['link', 'image', 'video'],
|
|
75
|
+
['fullScreen', 'codeView'],
|
|
76
|
+
];
|
|
77
|
+
const editorOptions = {
|
|
78
|
+
lang: lang.ko,
|
|
79
|
+
plugins: {
|
|
80
|
+
blockquote,
|
|
81
|
+
align,
|
|
82
|
+
font,
|
|
83
|
+
fontSize,
|
|
84
|
+
fontColor,
|
|
85
|
+
hiliteColor,
|
|
86
|
+
horizontalRule,
|
|
87
|
+
list,
|
|
88
|
+
table,
|
|
89
|
+
formatBlock,
|
|
90
|
+
lineHeight,
|
|
91
|
+
template,
|
|
92
|
+
paragraphStyle,
|
|
93
|
+
textStyle,
|
|
94
|
+
link,
|
|
95
|
+
image,
|
|
96
|
+
video,
|
|
97
|
+
},
|
|
98
|
+
width: width,
|
|
99
|
+
height: height,
|
|
100
|
+
buttonList: toolbar,
|
|
101
|
+
formats: [
|
|
102
|
+
'h1',
|
|
103
|
+
'h2',
|
|
104
|
+
'h3',
|
|
105
|
+
{
|
|
106
|
+
tag: 'p',
|
|
107
|
+
name: '๋ณธ๋ฌธ',
|
|
108
|
+
command: 'replace',
|
|
109
|
+
class: '',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
tag: 'div',
|
|
113
|
+
name: '์ธ์ฉ๊ตฌ',
|
|
114
|
+
command: 'range',
|
|
115
|
+
class: '__se__format__range__quote',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
resizingBar: false,
|
|
119
|
+
imageUploadUrl,
|
|
120
|
+
imageUploadHeader,
|
|
121
|
+
icons: {
|
|
122
|
+
undo: '<i class="editor undo">์คํ์ทจ์</i>',
|
|
123
|
+
redo: '<i class="editor redo">๋ค์์คํ</i>',
|
|
124
|
+
bold: '<i class="editor bold">๊ตต๊ฒ</i>',
|
|
125
|
+
underline: '<i class="editor underline">๋ฐ์ค</i>',
|
|
126
|
+
italic: '<i class="editor italic">๊ธฐ์ธ์ด๊ธฐ</i>',
|
|
127
|
+
strike: '<i class="editor strike">๊ฐ์ด๋ฐ์ </i>',
|
|
128
|
+
font_color: '<i class="editor font">๊ธ์์</i>',
|
|
129
|
+
highlight_color: '<i class="editor highlighter">๋ฐฐ๊ฒฝ์</i>',
|
|
130
|
+
align_left: '<i class="editor align-left">์ผ์ชฝ ์ ๋ ฌ</i>',
|
|
131
|
+
align_center: '<i class="editor align-center">๊ฐ์ด๋ฐ ์ ๋ ฌ</i>',
|
|
132
|
+
align_right: '<i class="editor align-right">์ฐ์ธก ์ ๋ ฌ</i>',
|
|
133
|
+
align_justify: '<i class="editor align-justify">์์ชฝ ์ ๋ ฌ</i>',
|
|
134
|
+
|
|
135
|
+
list_bullets: '<i class="editor list-ul">๋ชฉ๋ก</i>',
|
|
136
|
+
list_number: '<i class="editor list-ol">์ซ์ ๋ชฉ๋ก</i>',
|
|
137
|
+
table: '<i class="editor table">ํ
์ด๋ธ</i>',
|
|
138
|
+
link: '<i class="editor link">๋งํฌ</i>',
|
|
139
|
+
image: '<i class="editor image">์ฌ์ง</i>',
|
|
140
|
+
video: '<i class="editor video">๋น๋์ค</i>',
|
|
141
|
+
expansion: '<i class="editor expand">ํ์ฅ</i>',
|
|
142
|
+
code_view: '<i class="editor code">์ฝ๋</i>',
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
148
|
+
ref.current = suneditor.create(editorID, editorOptions as any);
|
|
149
|
+
|
|
150
|
+
ref.current.onChange = function (content: string) {
|
|
151
|
+
onChange(content);
|
|
152
|
+
validateHandler(content);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return () => {
|
|
156
|
+
ref.current.destroy();
|
|
157
|
+
};
|
|
158
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
159
|
+
}, []);
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div className={`${styles.editor} ${statusClass}`}>
|
|
163
|
+
<textarea ref={editorRef} id={editorID} defaultValue={value} />
|
|
164
|
+
{validator && message && (
|
|
165
|
+
<div className={styles.validator}>{message}</div>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export default SunEditor;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
@use '../../mixin.scss' as *;
|
|
2
|
+
.style {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: s(3);
|
|
6
|
+
&:global {
|
|
7
|
+
> input {
|
|
8
|
+
width: 300px;
|
|
9
|
+
}
|
|
10
|
+
> div.validator {
|
|
11
|
+
@include p4;
|
|
12
|
+
& {
|
|
13
|
+
color: color('text-sub');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
> div{
|
|
17
|
+
position: relative;
|
|
18
|
+
width: fit-content;
|
|
19
|
+
> span.unit {
|
|
20
|
+
position: absolute;
|
|
21
|
+
right: s(3);
|
|
22
|
+
top: 50%;
|
|
23
|
+
transform: translateY(-50%);
|
|
24
|
+
color: color('text-sub');
|
|
25
|
+
pointer-events: none;
|
|
26
|
+
}
|
|
27
|
+
> input {
|
|
28
|
+
/* Chrome, Safari, Edge, Opera */
|
|
29
|
+
&::-webkit-outer-spin-button,
|
|
30
|
+
&::-webkit-inner-spin-button {
|
|
31
|
+
-webkit-appearance: none;
|
|
32
|
+
margin: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Firefox */
|
|
36
|
+
&[type='number'] {
|
|
37
|
+
-moz-appearance: textfield;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import styles from './input.module.scss';
|
|
4
|
+
|
|
5
|
+
interface InputWrapperProps extends React.ComponentProps<'input'> {
|
|
6
|
+
value?: string | number;
|
|
7
|
+
className?: string;
|
|
8
|
+
validator?: z.ZodType<unknown>;
|
|
9
|
+
withIcon?: string;
|
|
10
|
+
withRightIcon?: string;
|
|
11
|
+
unit?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Input: React.FC<InputWrapperProps> = ({
|
|
15
|
+
validator,
|
|
16
|
+
value,
|
|
17
|
+
className,
|
|
18
|
+
withIcon,
|
|
19
|
+
withRightIcon,
|
|
20
|
+
unit,
|
|
21
|
+
...rest
|
|
22
|
+
}) => {
|
|
23
|
+
const [message, setMessage] = useState('');
|
|
24
|
+
const [statusClass, setStatusClass] = useState('');
|
|
25
|
+
|
|
26
|
+
const validateHandler = useCallback(() => {
|
|
27
|
+
setMessage('');
|
|
28
|
+
setStatusClass('');
|
|
29
|
+
if (validator && value) {
|
|
30
|
+
try {
|
|
31
|
+
validator.parse(value);
|
|
32
|
+
setStatusClass('success');
|
|
33
|
+
} catch (e) {
|
|
34
|
+
if (e instanceof z.ZodError) {
|
|
35
|
+
setMessage(e.errors[0].message);
|
|
36
|
+
setStatusClass('danger');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}, [validator, value]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
validateHandler();
|
|
44
|
+
}, [validateHandler, value]);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className={`${styles.style} ${className || ''}`}>
|
|
48
|
+
<div
|
|
49
|
+
className={`${className || ''} ${withIcon ? 'with-icon' : ''} ${withRightIcon ? 'with-right-icon' : ''}`}
|
|
50
|
+
>
|
|
51
|
+
{withIcon && <i className={withIcon} />}
|
|
52
|
+
<input
|
|
53
|
+
{...rest}
|
|
54
|
+
value={value ?? ''}
|
|
55
|
+
className={`${statusClass} ${className || ''}`}
|
|
56
|
+
/>
|
|
57
|
+
{withRightIcon && <i className={withRightIcon} />}
|
|
58
|
+
{unit && <span className="unit">{unit}</span>}
|
|
59
|
+
</div>
|
|
60
|
+
{validator && message !== '' && (
|
|
61
|
+
<div className="validator">{message}</div>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default Input;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import styles from './textarea.module.scss';
|
|
4
|
+
|
|
5
|
+
interface TextareaWrapperProps extends React.ComponentProps<'textarea'> {
|
|
6
|
+
value: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
validator?: z.ZodType<unknown>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Textarea: React.FC<TextareaWrapperProps> = ({
|
|
12
|
+
validator,
|
|
13
|
+
value,
|
|
14
|
+
className,
|
|
15
|
+
...rest
|
|
16
|
+
}) => {
|
|
17
|
+
const [message, setMessage] = useState('');
|
|
18
|
+
const [statusClass, setStatusClass] = useState('');
|
|
19
|
+
|
|
20
|
+
const validateHandler = () => {
|
|
21
|
+
setMessage('');
|
|
22
|
+
setStatusClass('');
|
|
23
|
+
if (validator && value.length > 0) {
|
|
24
|
+
try {
|
|
25
|
+
validator.parse(value);
|
|
26
|
+
setStatusClass('success');
|
|
27
|
+
} catch (e) {
|
|
28
|
+
if (e instanceof z.ZodError) {
|
|
29
|
+
setMessage(e.errors[0].message);
|
|
30
|
+
setStatusClass('danger');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={`${styles.style} ${className}`}>
|
|
38
|
+
<textarea
|
|
39
|
+
{...rest}
|
|
40
|
+
value={value}
|
|
41
|
+
className={`${statusClass} ${className}`}
|
|
42
|
+
onKeyUp={validateHandler}
|
|
43
|
+
/>
|
|
44
|
+
{validator && message !== '' && (
|
|
45
|
+
<div className="validator">{message}</div>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default Textarea;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
@use '../../mixin.scss' as *;
|
|
2
|
+
.style {
|
|
3
|
+
width: 100%;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: s(3);
|
|
7
|
+
&:global {
|
|
8
|
+
> div.child {
|
|
9
|
+
width: 100%;
|
|
10
|
+
> :not(:last-child) {
|
|
11
|
+
display: inline-block;
|
|
12
|
+
margin-right: s(5);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
> div.helper {
|
|
16
|
+
@include p4;
|
|
17
|
+
& {
|
|
18
|
+
color: color('text-sub');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import styles from './field.module.scss';
|
|
3
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
label?: string;
|
|
7
|
+
labelClass?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
helper?: string;
|
|
10
|
+
helperClass?: string;
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
validator?: z.ZodType<unknown>;
|
|
13
|
+
value?: string;
|
|
14
|
+
setClassName?: React.Dispatch<React.SetStateAction<string>>;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Field = ({
|
|
19
|
+
label,
|
|
20
|
+
labelClass,
|
|
21
|
+
required,
|
|
22
|
+
helper,
|
|
23
|
+
helperClass,
|
|
24
|
+
children,
|
|
25
|
+
validator,
|
|
26
|
+
value,
|
|
27
|
+
setClassName,
|
|
28
|
+
className,
|
|
29
|
+
}: Props) => {
|
|
30
|
+
const [message, setMessage] = useState('');
|
|
31
|
+
|
|
32
|
+
const validateHandler = useCallback(() => {
|
|
33
|
+
setMessage('');
|
|
34
|
+
if (setClassName) {
|
|
35
|
+
setClassName('');
|
|
36
|
+
}
|
|
37
|
+
if (validator && value && value.length > 0) {
|
|
38
|
+
try {
|
|
39
|
+
validator.parse(value);
|
|
40
|
+
if (setClassName) {
|
|
41
|
+
setClassName('success');
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
if (e instanceof z.ZodError) {
|
|
45
|
+
setMessage(e.errors[0].message);
|
|
46
|
+
if (setClassName) {
|
|
47
|
+
setClassName('danger');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}, [validator, value, setClassName]);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
validateHandler();
|
|
56
|
+
}, [validateHandler]);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className={`${styles.style} ${className || ''}`}>
|
|
60
|
+
{label && (
|
|
61
|
+
<label className={labelClass}>
|
|
62
|
+
{label}
|
|
63
|
+
|
|
64
|
+
{required && <span className="required">*</span>}
|
|
65
|
+
</label>
|
|
66
|
+
)}
|
|
67
|
+
<div className="child">{children}</div>
|
|
68
|
+
{helper ||
|
|
69
|
+
(validator && message !== '' && (
|
|
70
|
+
<div className={`helper ${helperClass || ''}`}>
|
|
71
|
+
{message || helper}
|
|
72
|
+
</div>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default Field;
|
package/react.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Input from './react/atom/input';
|
|
2
|
+
import Textarea from './react/atom/textarea';
|
|
3
|
+
import Editor from './react/atom/editor';
|
|
4
|
+
import EditorView from './react/atom/editor-view';
|
|
5
|
+
import Field from './react/molecule/field';
|
|
6
|
+
const Form = {
|
|
7
|
+
Input,
|
|
8
|
+
Textarea,
|
|
9
|
+
Editor,
|
|
10
|
+
EditorView,
|
|
11
|
+
Field,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default Form;
|
|
15
|
+
|
|
16
|
+
export { Input, Textarea, Editor, EditorView, Field };
|
package/readme.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Podo-ui
|
|
2
|
+
- SCSS Module ๊ธฐ๋ฐ ๋์์ธ ์์คํ
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# SCSS
|
|
6
|
+
|
|
7
|
+
## ๊ธฐ๋ณธ ์ ์ฉ
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import 'podo-ui/global.scss';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### SCSS Module ๋ด์์ ๋ณ์, ํจ์, ๋ฏน์ค์ธ ํ์ฉ
|
|
14
|
+
|
|
15
|
+
๐ ํ์ผ์ ์ต์๋จ์ ์๋ ํ์ผ ์ ์ธ
|
|
16
|
+
|
|
17
|
+
```scss
|
|
18
|
+
@use 'podo-ui/mixin' as *;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Variables
|
|
22
|
+
|
|
23
|
+
### Color Tone
|
|
24
|
+
|
|
25
|
+
๐ ํ์์ ํค์ ๊ฒฐ์ ํจ. ๊ธฐ๋ณธ๊ฐ(๋ฏธ์ค์ ) ๊ณผ warm์ผ๋ก ์ ํํ ์ ์์.
|
|
26
|
+
๐ js์์ ์๋์ ๊ฐ์ด ์ค์ (warm)
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
// ๊ธฐ๋ณธ๊ฐ
|
|
30
|
+
document.documentElement.setAttribute('data-color-tone', '');
|
|
31
|
+
|
|
32
|
+
// warm ํค
|
|
33
|
+
document.documentElement.setAttribute('data-color-tone', 'warm');
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Dark Mode
|
|
37
|
+
|
|
38
|
+
๐ ๋คํฌ๋ชจ๋๋ฅผ ์ค์ ํจ. ๊ธฐ๋ณธ๊ฐ(์๋, ๋ธ๋ผ์ฐ์ ์ค์ ์ ๋ฐ๋ฆ)๊ณผ 'light', 'dark'๋ฅผ ์ ํํ ์ ์์.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
// ๊ธฐ๋ณธ๊ฐ(์๋)
|
|
42
|
+
document.documentElement.setAttribute('data-color-mode', '');
|
|
43
|
+
|
|
44
|
+
// light ๋ชจ๋
|
|
45
|
+
document.documentElement.setAttribute('data-color-mode', 'light');
|
|
46
|
+
|
|
47
|
+
// dark ๋ชจ๋
|
|
48
|
+
document.documentElement.setAttribute('data-color-mode', 'dark');
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Font family
|
|
52
|
+
|
|
53
|
+
๐ font-family๋ฅผ ์ค์ . scss์์ ๋ฎ์ด์ฐ๊ธฐ
|
|
54
|
+
|
|
55
|
+
```scss
|
|
56
|
+
:root {
|
|
57
|
+
--base-font-family: 'Pretendard', sans-serif;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## ์์
|
|
62
|
+
|
|
63
|
+
### ์ ์ฉ ์์ ์ข
๋ฅ
|
|
64
|
+
|
|
65
|
+
๐ ์ ํ ๊ฐ๋ฅํ ์์ ์ข
๋ฅ : primary, default, default-deep, info, link, success, warning, danger
|
|
66
|
+
๐ ์ ํ ๊ฐ๋ฅํ ์์์ ํ์ ์์์ผ๋ก ์์๋ช
-fill๊ณผ ์์๋ช
-reverse๊ฐ ์กด์ฌํ๋ค.
|
|
67
|
+
๐ ์ ์ ํ์์ ์๋ text, border, bg, bg-elevation์ ์์คํ
๋ด ์๋ ์ ์ฉํ๋ค.
|
|
68
|
+
|
|
69
|
+
### CSS ์ ํ์ ์ฌ์ฉ
|
|
70
|
+
|
|
71
|
+
๐ ์๋์ ๊ฐ์ด ์ง์ ์ ํ์๋ฅผ ๋ฃ์ด ์ฌ์ฉ
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<div className="primary">primary color</div>
|
|
75
|
+
<div className="bg-primary">primary background color</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### SCSS ๋ด์์ ํจ์ ์ฌ์ฉ
|
|
79
|
+
|
|
80
|
+
```scss
|
|
81
|
+
p {
|
|
82
|
+
color: color(๊ฐ);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### ์ปค์ค์ปด ์ปฌ๋ฌ
|
|
87
|
+
|
|
88
|
+
๐ ์๋ ํํ๋ก css ๋ณ์๋ฅผ ์ปค์คํ
ํ์ฌ ์ฌ์ฉ
|
|
89
|
+
|
|
90
|
+
```scss
|
|
91
|
+
:root {
|
|
92
|
+
--color-primary: #f34a35;
|
|
93
|
+
--color-primary-hover: #dd2d0f;
|
|
94
|
+
--color-primary-pressed: #c9280c;
|
|
95
|
+
--color-primary-focus: #dd2d0f;
|
|
96
|
+
--color-primary-fill: #fff8f7;
|
|
97
|
+
--color-primary-reverse: #ffffff;
|
|
98
|
+
}
|
|
99
|
+
html[data-color-mode='dark'] {
|
|
100
|
+
--color-primary: #ff5d49;
|
|
101
|
+
--color-primary-hover: #ff7564;
|
|
102
|
+
--color-primary-pressed: #ff5d49;
|
|
103
|
+
--color-primary-focus: #ff7564;
|
|
104
|
+
--color-primary-fill: #1c1c20;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## ๊ทธ๋ฆฌ๋
|
|
109
|
+
|
|
110
|
+
### ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋ ์์คํ
( 4 ~ 12 columns / auto wrap)
|
|
111
|
+
|
|
112
|
+
๐ css์์ .grid ์์ฑ์ผ๋ก ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋ ์์คํ
์ ์ค์ ํฉ๋๋ค.
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<section className="grid">
|
|
116
|
+
<div className="w-4">w-4</div>
|
|
117
|
+
<div className="w-4">w-4</div>
|
|
118
|
+
<div className="w-4">w-4</div>
|
|
119
|
+
</section>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
๐ ๊ทธ๋ฆฌ๋ ๋ด ํ์ ์์๊ฐ ์ค๋จ์ ์ด์์ผ ๊ฒฝ์ฐ ์๋์ผ๋ก ์ค๋ฐ๊ฟ ํฉ๋๋ค.
|
|
123
|
+
๐ w-{์ซ์} ํํ๋ก ์ด๋ฃจ์ด์ ธ์์ผ๋ฉฐ 1~12 ๊ทธ๋ฆฌ๋๊ฐ ์กด์ฌํฉ๋๋ค.
|
|
124
|
+
๐ {์ซ์} / 12 \* 100% ํ ๊ฐ์ ์ ์ฒด ๊ทธ๋ฆฌ๋ ๋ด์ ๋น์จ๋ก ์ฌ์ฉํฉ๋๋ค.
|
|
125
|
+
๐ ์: w-1์ 1 / 12 \* 100 = 8.333% ํฌ๊ธฐ, w-6์ 6 / 12 \* 100 = 50% ํฌ๊ธฐ๋ฅผ ์ฐจ์งํฉ๋๋ค.
|
|
126
|
+
๐ PC๋ 12 ๊ทธ๋ฆฌ๋, Tablet์ 6 ๊ทธ๋ฆฌ๋, Mobile์ 4 ๊ทธ๋ฆฌ๋๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ํ๋ค.
|
|
127
|
+
|
|
128
|
+
### ๊ณ ์ ๊ทธ๋ฆฌ๋ ์์คํ
|
|
129
|
+
|
|
130
|
+
๐ ๋๋ฐ์ด์ค์ ๊ด๊ณ์์ด ๋๋ฐ์ด์ค์ ๋๋น๋ฅผ ์ ์งํ๊ณ ์ค๋ฐ๊ฟ์ ํ์ง ์์ต๋๋ค.
|
|
131
|
+
๐ css์์ grid-fix-2 ~ grid-gix-6 ๋ก 2 ~ 6๊ฐ์ ์ปฌ๋ผ์ ๊ฐ์ง ๊ทธ๋ฆฌ๋ ์์คํ
์ ์ค์ ํฉ๋๋ค.
|
|
132
|
+
๐ 4๊ทธ๋ฆฌ๋์ผ ๊ฒฝ์ฐ CSS์์ ํ์ ๊ตฌ์ฑ์์๋ w-1_4, w-2_4, w-3_4๊ฐ ์กด์ฌํฉ๋๋ค.w-1_4์ 1/4(25%)
|
|
133
|
+
๐ ํฌ๊ธฐ, w-2_4๋ 2/4(50%) ํฌ๊ธฐ, w-3_4๋ 3/4(75%) ํฌ๊ธฐ๋ฅผ ์ฐจ์งํฉ๋๋ค.
|
|
134
|
+
๐ Fixed wrap grid์์๋ w-full๋ก ํ์ ๊ตฌ์ฑ์์๋ฅผ ๊ทธ๋ฆฌ๋ ์์คํ
๋ด์ 100% ํฌ๊ธฐ๋ฅผ ์ฐจ์งํ๋๋ก ์ค์ ํ ์ ์์ต๋๋ค.
|
|
135
|
+
|
|
136
|
+
## ๋ ์ด์์
|
|
137
|
+
|
|
138
|
+
### ํ
๋๋ฆฌ ๋๊ป (border)
|
|
139
|
+
|
|
140
|
+
๐ border ํจ์ ํน์ class ์ฌ์ฉ ์ฌ์ฉ (0 ~ 4)
|
|
141
|
+
|
|
142
|
+
```scss
|
|
143
|
+
p {
|
|
144
|
+
margin: border(๊ฐ);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```html
|
|
149
|
+
<p className="border-4">border-4</p>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### ํ
๋๋ฆฌ ๋ฐ๊ฒฝ (border radius)
|
|
153
|
+
|
|
154
|
+
๐ r ํจ์ ํน์ class ์ฌ์ฉ (0 ~ 6, 'full')
|
|
155
|
+
|
|
156
|
+
```scss
|
|
157
|
+
p {
|
|
158
|
+
margin: r(๊ฐ);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```html
|
|
163
|
+
<p className="r-4">r-4</p>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### ์ฌ๋ฐฑ (spacing)
|
|
167
|
+
|
|
168
|
+
๐ spacing ํจ์ ํน์ class ์ฌ์ฉ (0 ~ 13)
|
|
169
|
+
|
|
170
|
+
```scss
|
|
171
|
+
p {
|
|
172
|
+
margin: s(๊ฐ);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<p className="spacing-4">spacing-4</p>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## ์
๋ ฅ์์
|
|
181
|
+
|
|
182
|
+
### ํ ๊ธ (toggle)
|
|
183
|
+
|
|
184
|
+
๐ checkbox์ .toggle ํด๋์ค ์ฌ์ฉ
|
|
185
|
+
|
|
186
|
+
```html
|
|
187
|
+
<input type="checkbox" className="toggle" />
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## ์์ด์ฝ ์์ฑ ๋ฐฉ๋ฒ
|
|
191
|
+
|
|
192
|
+
๐ ๋์์ธ ์์คํ
ํผ๊ทธ๋ง์์ ์ํ๋ฅผ ์ ํํ ํ svg๋ก export ํ ๊ฒ
|
|
193
|
+
๐ ํฐํธํฌ์ง์์ icon.woff ํฐํธ ์์ฑ
|
|
194
|
+
๐ /scss/icon/font ๊ฒฝ๋ก์ icon.woff ๋ณต์ฌ
|
|
195
|
+
๐ ์์์ ์๋ ์คํฌ๋ฆฝํธ ์คํ
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
npm run icon
|
|
199
|
+
```
|