@tipp/ui-quill-editor 1.0.1

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 (77) hide show
  1. package/README.md +3 -0
  2. package/dist/chunk-5WZL7EM7.js +272 -0
  3. package/dist/chunk-5WZL7EM7.js.map +1 -0
  4. package/dist/chunk-63UMKKIY.js +254 -0
  5. package/dist/chunk-63UMKKIY.js.map +1 -0
  6. package/dist/chunk-6Z3DIHNP.js +253 -0
  7. package/dist/chunk-6Z3DIHNP.js.map +1 -0
  8. package/dist/chunk-ACJWK26B.js +272 -0
  9. package/dist/chunk-ACJWK26B.js.map +1 -0
  10. package/dist/chunk-AEDRFU3N.js +273 -0
  11. package/dist/chunk-AEDRFU3N.js.map +1 -0
  12. package/dist/chunk-CCOIRSYJ.js +258 -0
  13. package/dist/chunk-CCOIRSYJ.js.map +1 -0
  14. package/dist/chunk-EQCXRM42.js +253 -0
  15. package/dist/chunk-EQCXRM42.js.map +1 -0
  16. package/dist/chunk-FFENCIRE.js +254 -0
  17. package/dist/chunk-FFENCIRE.js.map +1 -0
  18. package/dist/chunk-HZN6OVD6.js +253 -0
  19. package/dist/chunk-HZN6OVD6.js.map +1 -0
  20. package/dist/chunk-ICAKLBT5.js +253 -0
  21. package/dist/chunk-ICAKLBT5.js.map +1 -0
  22. package/dist/chunk-IIBNGFLN.mjs +273 -0
  23. package/dist/chunk-IIBNGFLN.mjs.map +1 -0
  24. package/dist/chunk-K4Q6PRQ3.js +272 -0
  25. package/dist/chunk-K4Q6PRQ3.js.map +1 -0
  26. package/dist/chunk-KBRYMAW4.js +254 -0
  27. package/dist/chunk-KBRYMAW4.js.map +1 -0
  28. package/dist/chunk-KYAHTSA6.js +253 -0
  29. package/dist/chunk-KYAHTSA6.js.map +1 -0
  30. package/dist/chunk-NQOX4TPA.js +258 -0
  31. package/dist/chunk-NQOX4TPA.js.map +1 -0
  32. package/dist/chunk-OLRR6W3N.js +272 -0
  33. package/dist/chunk-OLRR6W3N.js.map +1 -0
  34. package/dist/chunk-PSI2XOKW.js +272 -0
  35. package/dist/chunk-PSI2XOKW.js.map +1 -0
  36. package/dist/chunk-PWBZK7KO.js +272 -0
  37. package/dist/chunk-PWBZK7KO.js.map +1 -0
  38. package/dist/chunk-QAD3RXYR.js +253 -0
  39. package/dist/chunk-QAD3RXYR.js.map +1 -0
  40. package/dist/chunk-QGCFWTOT.js +253 -0
  41. package/dist/chunk-QGCFWTOT.js.map +1 -0
  42. package/dist/chunk-QVPYAKUW.js +253 -0
  43. package/dist/chunk-QVPYAKUW.js.map +1 -0
  44. package/dist/chunk-RC5DDS52.js +272 -0
  45. package/dist/chunk-RC5DDS52.js.map +1 -0
  46. package/dist/chunk-VWJC7GOO.js +253 -0
  47. package/dist/chunk-VWJC7GOO.js.map +1 -0
  48. package/dist/chunk-WQQTLSPU.js +253 -0
  49. package/dist/chunk-WQQTLSPU.js.map +1 -0
  50. package/dist/chunk-X5ODDFPU.js +272 -0
  51. package/dist/chunk-X5ODDFPU.js.map +1 -0
  52. package/dist/chunk-XFOLCHPG.js +257 -0
  53. package/dist/chunk-XFOLCHPG.js.map +1 -0
  54. package/dist/chunk-XO4BXXHE.js +258 -0
  55. package/dist/chunk-XO4BXXHE.js.map +1 -0
  56. package/dist/editor.cjs +272 -0
  57. package/dist/editor.cjs.map +1 -0
  58. package/dist/editor.d.cts +30 -0
  59. package/dist/editor.d.mts +30 -0
  60. package/dist/editor.d.ts +30 -0
  61. package/dist/editor.js +7 -0
  62. package/dist/editor.js.map +1 -0
  63. package/dist/editor.mjs +7 -0
  64. package/dist/editor.mjs.map +1 -0
  65. package/dist/index.cjs +274 -0
  66. package/dist/index.cjs.map +1 -0
  67. package/dist/index.css +971 -0
  68. package/dist/index.d.cts +3 -0
  69. package/dist/index.d.mts +3 -0
  70. package/dist/index.d.ts +3 -0
  71. package/dist/index.js +7 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/index.mjs +7 -0
  74. package/dist/index.mjs.map +1 -0
  75. package/package.json +64 -0
  76. package/src/editor.tsx +236 -0
  77. package/src/index.tsx +1 -0
@@ -0,0 +1,3 @@
1
+ export { Editor } from './editor.cjs';
2
+ import 'react';
3
+ import 'react-quill';
@@ -0,0 +1,3 @@
1
+ export { Editor } from './editor.mjs';
2
+ import 'react';
3
+ import 'react-quill';
@@ -0,0 +1,3 @@
1
+ export { Editor } from './editor.js';
2
+ import 'react';
3
+ import 'react-quill';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ Editor
3
+ } from "./chunk-ICAKLBT5.js";
4
+ export {
5
+ Editor
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ Editor
3
+ } from "./chunk-IIBNGFLN.mjs";
4
+ export {
5
+ Editor
6
+ };
7
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@tipp/ui-quill-editor",
3
+ "version": "1.0.1",
4
+ "description": "tipp 디자인 시스템이 적용된 quillEditor 패키지, quillEditor의 사이즈가 커서 별도 패키지로 분리했습니다.",
5
+ "license": "ISC",
6
+ "author": "",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "require": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.cjs"
13
+ },
14
+ "import": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ },
19
+ "./*": "./*"
20
+ },
21
+ "main": "dist/index.js",
22
+ "types": "dist/index.d.ts",
23
+ "files": [
24
+ "src/**",
25
+ "*.css",
26
+ "dist/**"
27
+ ],
28
+ "dependencies": {
29
+ "react": "^18.3.1",
30
+ "react-dom": "^18.3.1",
31
+ "react-quill": "^2.0.0",
32
+ "@tipp/ui": "1.2.21"
33
+ },
34
+ "devDependencies": {
35
+ "@types/lodash-es": "^4.17.12",
36
+ "@types/react": "^18.2.61",
37
+ "@types/react-dom": "^18.2.19",
38
+ "autoprefixer": "10.4.19",
39
+ "eslint": "^8.57.0",
40
+ "postcss": "8.4.33",
41
+ "postcss-cli": "11.0.0",
42
+ "postcss-combine-duplicated-selectors": "10.0.3",
43
+ "postcss-custom-media": "10.0.2",
44
+ "postcss-discard-empty": "6.0.1",
45
+ "postcss-import": "16.0.0",
46
+ "postcss-nesting": "12.0.2",
47
+ "tsup": "^8.0.2",
48
+ "typescript": "^5.3.3",
49
+ "@tipp/eslint-config": "1.0.0",
50
+ "@tipp/typescript-config": "1.0.0"
51
+ },
52
+ "scripts": {
53
+ "build": "pnpm run build:js && pnpm run build:css",
54
+ "build:css": "postcss ./style/index.css -o dist/index.css",
55
+ "build:js": "tsup",
56
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
57
+ "dev": "pnpm run dev:js & pnpm run dev:css",
58
+ "dev:css": "pnpm run build:css --watch",
59
+ "dev:js": "pnpm run build:js --watch",
60
+ "lint": "eslint . --max-warnings 0",
61
+ "lint-staged": "lint-staged ./src",
62
+ "type-check": "tsc --noEmit"
63
+ }
64
+ }
package/src/editor.tsx ADDED
@@ -0,0 +1,236 @@
1
+ import React, { forwardRef, useCallback, useRef, useState } from 'react';
2
+ import {
3
+ Box,
4
+ Button,
5
+ Flex,
6
+ Grid,
7
+ Link,
8
+ Separator,
9
+ TextField,
10
+ Typo,
11
+ Link2Icon,
12
+ toast,
13
+ FileIcon,
14
+ } from '@tipp/ui';
15
+ import type { ReactQuillProps } from 'react-quill';
16
+ import ReactQuill from 'react-quill';
17
+
18
+ export interface Attachment {
19
+ fileName: string;
20
+ url: string;
21
+ }
22
+
23
+ export interface TippEditorProps extends ReactQuillProps {
24
+ defaultTitle?: string;
25
+ defaultValue?: string;
26
+ defaultAttachedFiles?: Attachment[];
27
+ /** 저장하기 버튼 클릭 시 실행 */
28
+ onClickSave?: (values: {
29
+ title: string;
30
+ content: string;
31
+ files: Attachment[];
32
+ }) => void;
33
+ /** 파일 업로드 버튼 클릭 시 실행 */
34
+ uploadFile?: (
35
+ file: File,
36
+ destination: string
37
+ ) => Promise<Attachment | undefined>;
38
+ deleteFile?: (fileUrl: string) => Promise<void>;
39
+ /** 외부에서 Editor를 빈 상태로 초기화 시켜야 할 때 사용 */
40
+ clearEditor?: React.MutableRefObject<(() => void) | undefined>;
41
+ /** 초기화 버튼말고 다른 버튼 추가시 */
42
+ SecondaryButton?: React.ReactNode;
43
+ /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */
44
+ isLoading?: boolean;
45
+ }
46
+
47
+ export const Editor = forwardRef<ReactQuill, TippEditorProps>(
48
+ (props, ref): React.ReactNode => {
49
+ const {
50
+ defaultAttachedFiles,
51
+ defaultTitle,
52
+ defaultValue,
53
+ onClickSave,
54
+ uploadFile,
55
+ deleteFile,
56
+ isLoading,
57
+ SecondaryButton,
58
+ clearEditor,
59
+ ...quillProps
60
+ } = props;
61
+ const defaultRef = useRef<ReactQuill>(null);
62
+ const editorRef = ref || defaultRef;
63
+ const [attachedFiles, setAttachedFiles] = useState<Attachment[]>(
64
+ defaultAttachedFiles || []
65
+ );
66
+ const [fileDeleteLoading, setFileDeleteLoading] = useState(new Set());
67
+
68
+ const [title, setTitle] = useState(defaultTitle || '');
69
+ const [content, setContent] = useState(defaultValue || '');
70
+
71
+ const handleOnChangeContent = useCallback((value: string) => {
72
+ setContent(value);
73
+ }, []);
74
+
75
+ const handleButtonClick = useCallback(() => {
76
+ let input: HTMLInputElement | null = document.createElement('input');
77
+ input.type = 'file';
78
+ input.onchange = async (event) => {
79
+ const file = (event.target as HTMLInputElement).files?.[0];
80
+ if (!file) {
81
+ // console.log('DEBUG: no file');
82
+ toast.error('파일을 선택해주세요.');
83
+ return;
84
+ }
85
+
86
+ const fileName = file.name;
87
+ const attachment = await uploadFile?.(file, `hr-notes/${fileName}`);
88
+ if (attachment) {
89
+ setAttachedFiles((prev) => [...prev, attachment]);
90
+ }
91
+ input = null;
92
+ };
93
+ input.click();
94
+ }, [uploadFile]);
95
+
96
+ const handleDeleteFile = useCallback(
97
+ async (fileUrl: string) => {
98
+ try {
99
+ setFileDeleteLoading((p) => p.add(fileUrl));
100
+ await deleteFile?.(fileUrl);
101
+ setAttachedFiles((currentFiles) =>
102
+ currentFiles.filter((item) => item.url !== fileUrl)
103
+ );
104
+ } catch (err) {
105
+ toast.error('파일 삭제에 실패했습니다.');
106
+ } finally {
107
+ setFileDeleteLoading((p) => {
108
+ p.delete(fileUrl);
109
+ return p;
110
+ });
111
+ }
112
+ },
113
+ [deleteFile]
114
+ );
115
+
116
+ const renderAttachedFiles = useCallback(() => {
117
+ return (
118
+ <Box width="100%">
119
+ {attachedFiles.map((file) => {
120
+ return (
121
+ <>
122
+ <Separator size="4" />
123
+ <Flex
124
+ align="center"
125
+ justify="between"
126
+ key={`${file.url}_${file.fileName}`}
127
+ p="4"
128
+ width="100%"
129
+ >
130
+ <Link href={file.url} size="2">
131
+ <Flex align="center" gap="3">
132
+ <FileIcon />
133
+ {file.fileName}
134
+ </Flex>
135
+ </Link>
136
+ <Button
137
+ loading={fileDeleteLoading.has(file.url)}
138
+ onClick={() => {
139
+ void handleDeleteFile(file.url);
140
+ }}
141
+ variant="ghost"
142
+ >
143
+ 첨부 파일 삭제
144
+ </Button>
145
+ </Flex>
146
+ </>
147
+ );
148
+ })}
149
+ </Box>
150
+ );
151
+ }, [attachedFiles, fileDeleteLoading, handleDeleteFile]);
152
+
153
+ const handleOnChangeTitle = useCallback<
154
+ React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>
155
+ >((e) => {
156
+ setTitle(e.target.value);
157
+ }, []);
158
+
159
+ const clearEditorState = useCallback(() => {
160
+ setContent('');
161
+ setAttachedFiles([]);
162
+ setTitle('');
163
+ }, []);
164
+
165
+ const handleSaveClick = useCallback(() => {
166
+ onClickSave?.({
167
+ title,
168
+ content,
169
+ files: attachedFiles,
170
+ });
171
+ }, [onClickSave, title, content, attachedFiles]);
172
+
173
+ if (props.clearEditor) {
174
+ props.clearEditor.current = clearEditorState;
175
+ }
176
+
177
+ return (
178
+ <div className="tipp-ql-wrapper">
179
+ {/* 제목 입력창 */}
180
+ <Grid
181
+ align="center"
182
+ columns="auto auto 1fr"
183
+ gap="2"
184
+ height="42px"
185
+ pl="2"
186
+ pr="3"
187
+ width="100%"
188
+ >
189
+ <Box pl="3" pr="3">
190
+ <Typo>제목</Typo>
191
+ </Box>
192
+ <Separator orientation="vertical" style={{ height: '100%' }} />
193
+ <TextField.Root
194
+ className="editor-title-text-field"
195
+ onChange={handleOnChangeTitle}
196
+ placeholder="제목을 입력해주세요"
197
+ value={title}
198
+ />
199
+ </Grid>
200
+ <ReactQuill
201
+ className="tipp-ql-editor write-mode"
202
+ onChange={handleOnChangeContent}
203
+ ref={editorRef}
204
+ theme="snow"
205
+ value={content}
206
+ {...quillProps}
207
+ />
208
+ {renderAttachedFiles()}
209
+ <Separator size="4" />
210
+ <Flex align="center" justify="between" p="2" pl="4" pr="4" width="100%">
211
+ <Button
212
+ color="gray"
213
+ onClick={handleButtonClick}
214
+ variant="transparent"
215
+ >
216
+ <Link2Icon height={20} width={20} />
217
+ </Button>
218
+
219
+ <Flex gap="2">
220
+ {clearEditor ? (
221
+ <Button color="gray" onClick={clearEditorState} variant="outline">
222
+ 초기화
223
+ </Button>
224
+ ) : null}
225
+ {SecondaryButton ? SecondaryButton : null}
226
+ <Button disabled={isLoading} onClick={handleSaveClick}>
227
+ 저장
228
+ </Button>
229
+ </Flex>
230
+ </Flex>
231
+ </div>
232
+ );
233
+ }
234
+ );
235
+
236
+ Editor.displayName = 'TIPP-Quill-Editor';
package/src/index.tsx ADDED
@@ -0,0 +1 @@
1
+ export { Editor } from './editor';