@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,258 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+ var __objRest = (source, exclude) => {
18
+ var target = {};
19
+ for (var prop in source)
20
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
21
+ target[prop] = source[prop];
22
+ if (source != null && __getOwnPropSymbols)
23
+ for (var prop of __getOwnPropSymbols(source)) {
24
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
25
+ target[prop] = source[prop];
26
+ }
27
+ return target;
28
+ };
29
+ var __async = (__this, __arguments, generator) => {
30
+ return new Promise((resolve, reject) => {
31
+ var fulfilled = (value) => {
32
+ try {
33
+ step(generator.next(value));
34
+ } catch (e) {
35
+ reject(e);
36
+ }
37
+ };
38
+ var rejected = (value) => {
39
+ try {
40
+ step(generator.throw(value));
41
+ } catch (e) {
42
+ reject(e);
43
+ }
44
+ };
45
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
46
+ step((generator = generator.apply(__this, __arguments)).next());
47
+ });
48
+ };
49
+
50
+ // src/editor.tsx
51
+ import { forwardRef, useCallback, useRef, useState } from "react";
52
+ import {
53
+ Box,
54
+ Button,
55
+ Flex,
56
+ Grid,
57
+ Link,
58
+ Separator,
59
+ TextField,
60
+ Typo,
61
+ Link2Icon,
62
+ toast,
63
+ FileIcon
64
+ } from "@tipp/ui";
65
+ import ReactQuill from "react-quill";
66
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
67
+ var Editor = forwardRef(
68
+ (props, ref) => {
69
+ const _a = props, {
70
+ defaultAttachedFiles,
71
+ defaultTitle,
72
+ defaultValue,
73
+ onClickSave,
74
+ uploadFile,
75
+ deleteFile,
76
+ isLoading,
77
+ SecondaryButton,
78
+ clearEditor
79
+ } = _a, quillProps = __objRest(_a, [
80
+ "defaultAttachedFiles",
81
+ "defaultTitle",
82
+ "defaultValue",
83
+ "onClickSave",
84
+ "uploadFile",
85
+ "deleteFile",
86
+ "isLoading",
87
+ "SecondaryButton",
88
+ "clearEditor"
89
+ ]);
90
+ const defaultRef = useRef(null);
91
+ const editorRef = ref || defaultRef;
92
+ const [attachedFiles, setAttachedFiles] = useState(
93
+ defaultAttachedFiles || []
94
+ );
95
+ const [fileDeleteLoading, setFileDeleteLoading] = useState(/* @__PURE__ */ new Set());
96
+ const [title, setTitle] = useState(defaultTitle || "");
97
+ const [content, _setContent] = useState(defaultValue || "");
98
+ const setContent = (v) => {
99
+ console.log("\uBB50\uC9C0?");
100
+ _setContent;
101
+ };
102
+ console.log({ content });
103
+ const handleOnChangeContent = useCallback((value) => {
104
+ setContent(value);
105
+ }, []);
106
+ const handleButtonClick = useCallback(() => {
107
+ let input = document.createElement("input");
108
+ input.type = "file";
109
+ input.onchange = (event) => __async(void 0, null, function* () {
110
+ var _a2;
111
+ const file = (_a2 = event.target.files) == null ? void 0 : _a2[0];
112
+ if (!file) {
113
+ toast.error("\uD30C\uC77C\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.");
114
+ return;
115
+ }
116
+ const fileName = file.name;
117
+ const attachment = yield uploadFile == null ? void 0 : uploadFile(file, `hr-notes/${fileName}`);
118
+ if (attachment) {
119
+ setAttachedFiles((prev) => [...prev, attachment]);
120
+ }
121
+ input = null;
122
+ });
123
+ input.click();
124
+ }, [uploadFile]);
125
+ const handleDeleteFile = useCallback(
126
+ (fileUrl) => __async(void 0, null, function* () {
127
+ try {
128
+ setFileDeleteLoading((p) => p.add(fileUrl));
129
+ yield deleteFile == null ? void 0 : deleteFile(fileUrl);
130
+ setAttachedFiles(
131
+ (currentFiles) => currentFiles.filter((item) => item.url !== fileUrl)
132
+ );
133
+ } catch (err) {
134
+ toast.error("\uD30C\uC77C \uC0AD\uC81C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
135
+ } finally {
136
+ setFileDeleteLoading((p) => {
137
+ p.delete(fileUrl);
138
+ return p;
139
+ });
140
+ }
141
+ }),
142
+ [deleteFile]
143
+ );
144
+ const renderAttachedFiles = useCallback(() => {
145
+ return /* @__PURE__ */ jsx(Box, { width: "100%", children: attachedFiles.map((file) => {
146
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
147
+ /* @__PURE__ */ jsx(Separator, { size: "4" }),
148
+ /* @__PURE__ */ jsxs(
149
+ Flex,
150
+ {
151
+ align: "center",
152
+ justify: "between",
153
+ p: "4",
154
+ width: "100%",
155
+ children: [
156
+ /* @__PURE__ */ jsx(Link, { href: file.url, size: "2", children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "3", children: [
157
+ /* @__PURE__ */ jsx(FileIcon, {}),
158
+ file.fileName
159
+ ] }) }),
160
+ /* @__PURE__ */ jsx(
161
+ Button,
162
+ {
163
+ loading: fileDeleteLoading.has(file.url),
164
+ onClick: () => {
165
+ void handleDeleteFile(file.url);
166
+ },
167
+ variant: "ghost",
168
+ children: "\uCCA8\uBD80 \uD30C\uC77C \uC0AD\uC81C"
169
+ }
170
+ )
171
+ ]
172
+ },
173
+ `${file.url}_${file.fileName}`
174
+ )
175
+ ] });
176
+ }) });
177
+ }, [attachedFiles, fileDeleteLoading, handleDeleteFile]);
178
+ const handleOnChangeTitle = useCallback((e) => {
179
+ setTitle(e.target.value);
180
+ }, []);
181
+ const clearEditorState = useCallback(() => {
182
+ setContent("");
183
+ setAttachedFiles([]);
184
+ setTitle("");
185
+ }, []);
186
+ const handleSaveClick = useCallback(() => {
187
+ onClickSave == null ? void 0 : onClickSave({
188
+ title,
189
+ content,
190
+ files: attachedFiles
191
+ });
192
+ }, [onClickSave, title, content, attachedFiles]);
193
+ if (props.clearEditor) {
194
+ props.clearEditor.current = clearEditorState;
195
+ }
196
+ return /* @__PURE__ */ jsxs("div", { className: "tipp-ql-wrapper", children: [
197
+ /* @__PURE__ */ jsxs(
198
+ Grid,
199
+ {
200
+ align: "center",
201
+ columns: "auto auto 1fr",
202
+ gap: "2",
203
+ height: "42px",
204
+ pl: "2",
205
+ pr: "3",
206
+ width: "100%",
207
+ children: [
208
+ /* @__PURE__ */ jsx(Box, { pl: "3", pr: "3", children: /* @__PURE__ */ jsx(Typo, { children: "\uC81C\uBAA9" }) }),
209
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", style: { height: "100%" } }),
210
+ /* @__PURE__ */ jsx(
211
+ TextField.Root,
212
+ {
213
+ className: "editor-title-text-field",
214
+ onChange: handleOnChangeTitle,
215
+ placeholder: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
216
+ value: title
217
+ }
218
+ )
219
+ ]
220
+ }
221
+ ),
222
+ /* @__PURE__ */ jsx(
223
+ ReactQuill,
224
+ __spreadValues({
225
+ className: "tipp-ql-editor write-mode",
226
+ onChange: handleOnChangeContent,
227
+ ref: editorRef,
228
+ theme: "snow",
229
+ value: content
230
+ }, quillProps)
231
+ ),
232
+ renderAttachedFiles(),
233
+ /* @__PURE__ */ jsx(Separator, { size: "4" }),
234
+ /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "between", p: "2", pl: "4", pr: "4", width: "100%", children: [
235
+ /* @__PURE__ */ jsx(
236
+ Button,
237
+ {
238
+ color: "gray",
239
+ onClick: handleButtonClick,
240
+ variant: "transparent",
241
+ children: /* @__PURE__ */ jsx(Link2Icon, { height: 20, width: 20 })
242
+ }
243
+ ),
244
+ /* @__PURE__ */ jsxs(Flex, { gap: "2", children: [
245
+ clearEditor ? /* @__PURE__ */ jsx(Button, { color: "gray", onClick: clearEditorState, variant: "outline", children: "\uCD08\uAE30\uD654" }) : null,
246
+ SecondaryButton ? SecondaryButton : null,
247
+ /* @__PURE__ */ jsx(Button, { disabled: isLoading, onClick: handleSaveClick, children: "\uC800\uC7A5" })
248
+ ] })
249
+ ] })
250
+ ] });
251
+ }
252
+ );
253
+ Editor.displayName = "TIPP-Quill-Editor";
254
+
255
+ export {
256
+ Editor
257
+ };
258
+ //# sourceMappingURL=chunk-XO4BXXHE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/editor.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useRef, useState } from 'react';\nimport {\n Box,\n Button,\n Flex,\n Grid,\n Link,\n Separator,\n TextField,\n Typo,\n Link2Icon,\n toast,\n FileIcon,\n} from '@tipp/ui';\nimport type { ReactQuillProps } from 'react-quill';\nimport ReactQuill from 'react-quill';\n\nexport interface Attachment {\n fileName: string;\n url: string;\n}\n\nexport interface TippEditorProps extends ReactQuillProps {\n defaultTitle?: string;\n defaultValue?: string;\n defaultAttachedFiles?: Attachment[];\n /** 저장하기 버튼 클릭 시 실행 */\n onClickSave?: (values: {\n title: string;\n content: string;\n files: Attachment[];\n }) => void;\n /** 파일 업로드 버튼 클릭 시 실행 */\n uploadFile?: (\n file: File,\n destination: string\n ) => Promise<Attachment | undefined>;\n deleteFile?: (fileUrl: string) => Promise<void>;\n /** 외부에서 Editor를 빈 상태로 초기화 시켜야 할 때 사용 */\n clearEditor?: React.MutableRefObject<(() => void) | undefined>;\n /** 초기화 버튼말고 다른 버튼 추가시 */\n SecondaryButton?: React.ReactNode;\n /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */\n isLoading?: boolean;\n}\n\nexport const Editor = forwardRef<ReactQuill, TippEditorProps>(\n (props, ref): React.ReactNode => {\n const {\n defaultAttachedFiles,\n defaultTitle,\n defaultValue,\n onClickSave,\n uploadFile,\n deleteFile,\n isLoading,\n SecondaryButton,\n clearEditor,\n ...quillProps\n } = props;\n const defaultRef = useRef<ReactQuill>(null);\n const editorRef = ref || defaultRef;\n const [attachedFiles, setAttachedFiles] = useState<Attachment[]>(\n defaultAttachedFiles || []\n );\n const [fileDeleteLoading, setFileDeleteLoading] = useState(new Set());\n\n const [title, setTitle] = useState(defaultTitle || '');\n const [content, _setContent] = useState(defaultValue || '');\n const setContent = (v: string) => {\n console.log('뭐지?');\n _setContent;\n };\n console.log({ content });\n const handleOnChangeContent = useCallback((value: string) => {\n setContent(value);\n }, []);\n\n const handleButtonClick = useCallback(() => {\n let input: HTMLInputElement | null = document.createElement('input');\n input.type = 'file';\n input.onchange = async (event) => {\n const file = (event.target as HTMLInputElement).files?.[0];\n if (!file) {\n // console.log('DEBUG: no file');\n toast.error('파일을 선택해주세요.');\n return;\n }\n\n const fileName = file.name;\n const attachment = await uploadFile?.(file, `hr-notes/${fileName}`);\n if (attachment) {\n setAttachedFiles((prev) => [...prev, attachment]);\n }\n input = null;\n };\n input.click();\n }, [uploadFile]);\n\n const handleDeleteFile = useCallback(\n async (fileUrl: string) => {\n try {\n setFileDeleteLoading((p) => p.add(fileUrl));\n await deleteFile?.(fileUrl);\n setAttachedFiles((currentFiles) =>\n currentFiles.filter((item) => item.url !== fileUrl)\n );\n } catch (err) {\n toast.error('파일 삭제에 실패했습니다.');\n } finally {\n setFileDeleteLoading((p) => {\n p.delete(fileUrl);\n return p;\n });\n }\n },\n [deleteFile]\n );\n\n const renderAttachedFiles = useCallback(() => {\n return (\n <Box width=\"100%\">\n {attachedFiles.map((file) => {\n return (\n <>\n <Separator size=\"4\" />\n <Flex\n align=\"center\"\n justify=\"between\"\n key={`${file.url}_${file.fileName}`}\n p=\"4\"\n width=\"100%\"\n >\n <Link href={file.url} size=\"2\">\n <Flex align=\"center\" gap=\"3\">\n <FileIcon />\n {file.fileName}\n </Flex>\n </Link>\n <Button\n loading={fileDeleteLoading.has(file.url)}\n onClick={() => {\n void handleDeleteFile(file.url);\n }}\n variant=\"ghost\"\n >\n 첨부 파일 삭제\n </Button>\n </Flex>\n </>\n );\n })}\n </Box>\n );\n }, [attachedFiles, fileDeleteLoading, handleDeleteFile]);\n\n const handleOnChangeTitle = useCallback<\n React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>\n >((e) => {\n setTitle(e.target.value);\n }, []);\n\n const clearEditorState = useCallback(() => {\n setContent('');\n setAttachedFiles([]);\n setTitle('');\n }, []);\n\n const handleSaveClick = useCallback(() => {\n onClickSave?.({\n title,\n content,\n files: attachedFiles,\n });\n }, [onClickSave, title, content, attachedFiles]);\n\n if (props.clearEditor) {\n props.clearEditor.current = clearEditorState;\n }\n\n return (\n <div className=\"tipp-ql-wrapper\">\n {/* 제목 입력창 */}\n <Grid\n align=\"center\"\n columns=\"auto auto 1fr\"\n gap=\"2\"\n height=\"42px\"\n pl=\"2\"\n pr=\"3\"\n width=\"100%\"\n >\n <Box pl=\"3\" pr=\"3\">\n <Typo>제목</Typo>\n </Box>\n <Separator orientation=\"vertical\" style={{ height: '100%' }} />\n <TextField.Root\n className=\"editor-title-text-field\"\n onChange={handleOnChangeTitle}\n placeholder=\"제목을 입력해주세요\"\n value={title}\n />\n </Grid>\n <ReactQuill\n className=\"tipp-ql-editor write-mode\"\n onChange={handleOnChangeContent}\n ref={editorRef}\n theme=\"snow\"\n value={content}\n {...quillProps}\n />\n {renderAttachedFiles()}\n <Separator size=\"4\" />\n <Flex align=\"center\" justify=\"between\" p=\"2\" pl=\"4\" pr=\"4\" width=\"100%\">\n <Button\n color=\"gray\"\n onClick={handleButtonClick}\n variant=\"transparent\"\n >\n <Link2Icon height={20} width={20} />\n </Button>\n\n <Flex gap=\"2\">\n {clearEditor ? (\n <Button color=\"gray\" onClick={clearEditorState} variant=\"outline\">\n 초기화\n </Button>\n ) : null}\n {SecondaryButton ? SecondaryButton : null}\n <Button disabled={isLoading} onClick={handleSaveClick}>\n 저장\n </Button>\n </Flex>\n </Flex>\n </div>\n );\n }\n);\n\nEditor.displayName = 'TIPP-Quill-Editor';\n"],"mappings":"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","names":["_a"]}
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __objRest = (source, exclude) => {
23
+ var target = {};
24
+ for (var prop in source)
25
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
26
+ target[prop] = source[prop];
27
+ if (source != null && __getOwnPropSymbols)
28
+ for (var prop of __getOwnPropSymbols(source)) {
29
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
30
+ target[prop] = source[prop];
31
+ }
32
+ return target;
33
+ };
34
+ var __export = (target, all) => {
35
+ for (var name in all)
36
+ __defProp(target, name, { get: all[name], enumerable: true });
37
+ };
38
+ var __copyProps = (to, from, except, desc) => {
39
+ if (from && typeof from === "object" || typeof from === "function") {
40
+ for (let key of __getOwnPropNames(from))
41
+ if (!__hasOwnProp.call(to, key) && key !== except)
42
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
43
+ }
44
+ return to;
45
+ };
46
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
47
+ // If the importer is in node compatibility mode or this is not an ESM
48
+ // file that has been converted to a CommonJS file using a Babel-
49
+ // compatible transform (i.e. "__esModule" has not been set), then set
50
+ // "default" to the CommonJS "module.exports" for node compatibility.
51
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
52
+ mod
53
+ ));
54
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
55
+ var __async = (__this, __arguments, generator) => {
56
+ return new Promise((resolve, reject) => {
57
+ var fulfilled = (value) => {
58
+ try {
59
+ step(generator.next(value));
60
+ } catch (e) {
61
+ reject(e);
62
+ }
63
+ };
64
+ var rejected = (value) => {
65
+ try {
66
+ step(generator.throw(value));
67
+ } catch (e) {
68
+ reject(e);
69
+ }
70
+ };
71
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
72
+ step((generator = generator.apply(__this, __arguments)).next());
73
+ });
74
+ };
75
+
76
+ // src/editor.tsx
77
+ var editor_exports = {};
78
+ __export(editor_exports, {
79
+ Editor: () => Editor
80
+ });
81
+ module.exports = __toCommonJS(editor_exports);
82
+ var import_react = require("react");
83
+ var import_ui = require("@tipp/ui");
84
+ var import_react_quill = __toESM(require("react-quill"), 1);
85
+ var import_jsx_runtime = require("react/jsx-runtime");
86
+ var Editor = (0, import_react.forwardRef)(
87
+ (props, ref) => {
88
+ const _a = props, {
89
+ defaultAttachedFiles,
90
+ defaultTitle,
91
+ defaultValue,
92
+ onClickSave,
93
+ uploadFile,
94
+ deleteFile,
95
+ isLoading,
96
+ SecondaryButton,
97
+ clearEditor
98
+ } = _a, quillProps = __objRest(_a, [
99
+ "defaultAttachedFiles",
100
+ "defaultTitle",
101
+ "defaultValue",
102
+ "onClickSave",
103
+ "uploadFile",
104
+ "deleteFile",
105
+ "isLoading",
106
+ "SecondaryButton",
107
+ "clearEditor"
108
+ ]);
109
+ const defaultRef = (0, import_react.useRef)(null);
110
+ const editorRef = ref || defaultRef;
111
+ const [attachedFiles, setAttachedFiles] = (0, import_react.useState)(
112
+ defaultAttachedFiles || []
113
+ );
114
+ const [fileDeleteLoading, setFileDeleteLoading] = (0, import_react.useState)(/* @__PURE__ */ new Set());
115
+ const [title, setTitle] = (0, import_react.useState)(defaultTitle || "");
116
+ const [content, setContent] = (0, import_react.useState)(defaultValue || "");
117
+ const handleOnChangeContent = (0, import_react.useCallback)((value) => {
118
+ setContent(value);
119
+ }, []);
120
+ const handleButtonClick = (0, import_react.useCallback)(() => {
121
+ let input = document.createElement("input");
122
+ input.type = "file";
123
+ input.onchange = (event) => __async(void 0, null, function* () {
124
+ var _a2;
125
+ const file = (_a2 = event.target.files) == null ? void 0 : _a2[0];
126
+ if (!file) {
127
+ import_ui.toast.error("\uD30C\uC77C\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.");
128
+ return;
129
+ }
130
+ const fileName = file.name;
131
+ const attachment = yield uploadFile == null ? void 0 : uploadFile(file, `hr-notes/${fileName}`);
132
+ if (attachment) {
133
+ setAttachedFiles((prev) => [...prev, attachment]);
134
+ }
135
+ input = null;
136
+ });
137
+ input.click();
138
+ }, [uploadFile]);
139
+ const handleDeleteFile = (0, import_react.useCallback)(
140
+ (fileUrl) => __async(void 0, null, function* () {
141
+ try {
142
+ setFileDeleteLoading((p) => p.add(fileUrl));
143
+ yield deleteFile == null ? void 0 : deleteFile(fileUrl);
144
+ setAttachedFiles(
145
+ (currentFiles) => currentFiles.filter((item) => item.url !== fileUrl)
146
+ );
147
+ } catch (err) {
148
+ import_ui.toast.error("\uD30C\uC77C \uC0AD\uC81C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
149
+ } finally {
150
+ setFileDeleteLoading((p) => {
151
+ p.delete(fileUrl);
152
+ return p;
153
+ });
154
+ }
155
+ }),
156
+ [deleteFile]
157
+ );
158
+ const renderAttachedFiles = (0, import_react.useCallback)(() => {
159
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Box, { width: "100%", children: attachedFiles.map((file) => {
160
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { size: "4" }),
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
163
+ import_ui.Flex,
164
+ {
165
+ align: "center",
166
+ justify: "between",
167
+ p: "4",
168
+ width: "100%",
169
+ children: [
170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Link, { href: file.url, size: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { align: "center", gap: "3", children: [
171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.FileIcon, {}),
172
+ file.fileName
173
+ ] }) }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
175
+ import_ui.Button,
176
+ {
177
+ loading: fileDeleteLoading.has(file.url),
178
+ onClick: () => {
179
+ void handleDeleteFile(file.url);
180
+ },
181
+ variant: "ghost",
182
+ children: "\uCCA8\uBD80 \uD30C\uC77C \uC0AD\uC81C"
183
+ }
184
+ )
185
+ ]
186
+ },
187
+ `${file.url}_${file.fileName}`
188
+ )
189
+ ] });
190
+ }) });
191
+ }, [attachedFiles, fileDeleteLoading, handleDeleteFile]);
192
+ const handleOnChangeTitle = (0, import_react.useCallback)((e) => {
193
+ setTitle(e.target.value);
194
+ }, []);
195
+ const clearEditorState = (0, import_react.useCallback)(() => {
196
+ setContent("");
197
+ setAttachedFiles([]);
198
+ setTitle("");
199
+ }, []);
200
+ const handleSaveClick = (0, import_react.useCallback)(() => {
201
+ onClickSave == null ? void 0 : onClickSave({
202
+ title,
203
+ content,
204
+ files: attachedFiles
205
+ });
206
+ }, [onClickSave, title, content, attachedFiles]);
207
+ if (props.clearEditor) {
208
+ props.clearEditor.current = clearEditorState;
209
+ }
210
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tipp-ql-wrapper", children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
212
+ import_ui.Grid,
213
+ {
214
+ align: "center",
215
+ columns: "auto auto 1fr",
216
+ gap: "2",
217
+ height: "42px",
218
+ pl: "2",
219
+ pr: "3",
220
+ width: "100%",
221
+ children: [
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Box, { pl: "3", pr: "3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Typo, { children: "\uC81C\uBAA9" }) }),
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { orientation: "vertical", style: { height: "100%" } }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
225
+ import_ui.TextField.Root,
226
+ {
227
+ className: "editor-title-text-field",
228
+ onChange: handleOnChangeTitle,
229
+ placeholder: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
230
+ value: title
231
+ }
232
+ )
233
+ ]
234
+ }
235
+ ),
236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
237
+ import_react_quill.default,
238
+ __spreadValues({
239
+ className: "tipp-ql-editor write-mode",
240
+ onChange: handleOnChangeContent,
241
+ ref: editorRef,
242
+ theme: "snow",
243
+ value: content
244
+ }, quillProps)
245
+ ),
246
+ renderAttachedFiles(),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { size: "4" }),
248
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { align: "center", justify: "between", p: "2", pl: "4", pr: "4", width: "100%", children: [
249
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
250
+ import_ui.Button,
251
+ {
252
+ color: "gray",
253
+ onClick: handleButtonClick,
254
+ variant: "transparent",
255
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Link2Icon, { height: 20, width: 20 })
256
+ }
257
+ ),
258
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { gap: "2", children: [
259
+ clearEditor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { color: "gray", onClick: clearEditorState, variant: "outline", children: "\uCD08\uAE30\uD654" }) : null,
260
+ SecondaryButton ? SecondaryButton : null,
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { disabled: isLoading, onClick: handleSaveClick, children: "\uC800\uC7A5" })
262
+ ] })
263
+ ] })
264
+ ] });
265
+ }
266
+ );
267
+ Editor.displayName = "TIPP-Quill-Editor";
268
+ // Annotate the CommonJS export names for ESM import in node:
269
+ 0 && (module.exports = {
270
+ Editor
271
+ });
272
+ //# sourceMappingURL=editor.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/editor.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useRef, useState } from 'react';\nimport {\n Box,\n Button,\n Flex,\n Grid,\n Link,\n Separator,\n TextField,\n Typo,\n Link2Icon,\n toast,\n FileIcon,\n} from '@tipp/ui';\nimport type { ReactQuillProps } from 'react-quill';\nimport ReactQuill from 'react-quill';\n\nexport interface Attachment {\n fileName: string;\n url: string;\n}\n\nexport interface TippEditorProps extends ReactQuillProps {\n defaultTitle?: string;\n defaultValue?: string;\n defaultAttachedFiles?: Attachment[];\n /** 저장하기 버튼 클릭 시 실행 */\n onClickSave?: (values: {\n title: string;\n content: string;\n files: Attachment[];\n }) => void;\n /** 파일 업로드 버튼 클릭 시 실행 */\n uploadFile?: (\n file: File,\n destination: string\n ) => Promise<Attachment | undefined>;\n deleteFile?: (fileUrl: string) => Promise<void>;\n /** 외부에서 Editor를 빈 상태로 초기화 시켜야 할 때 사용 */\n clearEditor?: React.MutableRefObject<(() => void) | undefined>;\n /** 초기화 버튼말고 다른 버튼 추가시 */\n SecondaryButton?: React.ReactNode;\n /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */\n isLoading?: boolean;\n}\n\nexport const Editor = forwardRef<ReactQuill, TippEditorProps>(\n (props, ref): React.ReactNode => {\n const {\n defaultAttachedFiles,\n defaultTitle,\n defaultValue,\n onClickSave,\n uploadFile,\n deleteFile,\n isLoading,\n SecondaryButton,\n clearEditor,\n ...quillProps\n } = props;\n const defaultRef = useRef<ReactQuill>(null);\n const editorRef = ref || defaultRef;\n const [attachedFiles, setAttachedFiles] = useState<Attachment[]>(\n defaultAttachedFiles || []\n );\n const [fileDeleteLoading, setFileDeleteLoading] = useState(new Set());\n\n const [title, setTitle] = useState(defaultTitle || '');\n const [content, setContent] = useState(defaultValue || '');\n\n const handleOnChangeContent = useCallback((value: string) => {\n setContent(value);\n }, []);\n\n const handleButtonClick = useCallback(() => {\n let input: HTMLInputElement | null = document.createElement('input');\n input.type = 'file';\n input.onchange = async (event) => {\n const file = (event.target as HTMLInputElement).files?.[0];\n if (!file) {\n // console.log('DEBUG: no file');\n toast.error('파일을 선택해주세요.');\n return;\n }\n\n const fileName = file.name;\n const attachment = await uploadFile?.(file, `hr-notes/${fileName}`);\n if (attachment) {\n setAttachedFiles((prev) => [...prev, attachment]);\n }\n input = null;\n };\n input.click();\n }, [uploadFile]);\n\n const handleDeleteFile = useCallback(\n async (fileUrl: string) => {\n try {\n setFileDeleteLoading((p) => p.add(fileUrl));\n await deleteFile?.(fileUrl);\n setAttachedFiles((currentFiles) =>\n currentFiles.filter((item) => item.url !== fileUrl)\n );\n } catch (err) {\n toast.error('파일 삭제에 실패했습니다.');\n } finally {\n setFileDeleteLoading((p) => {\n p.delete(fileUrl);\n return p;\n });\n }\n },\n [deleteFile]\n );\n\n const renderAttachedFiles = useCallback(() => {\n return (\n <Box width=\"100%\">\n {attachedFiles.map((file) => {\n return (\n <>\n <Separator size=\"4\" />\n <Flex\n align=\"center\"\n justify=\"between\"\n key={`${file.url}_${file.fileName}`}\n p=\"4\"\n width=\"100%\"\n >\n <Link href={file.url} size=\"2\">\n <Flex align=\"center\" gap=\"3\">\n <FileIcon />\n {file.fileName}\n </Flex>\n </Link>\n <Button\n loading={fileDeleteLoading.has(file.url)}\n onClick={() => {\n void handleDeleteFile(file.url);\n }}\n variant=\"ghost\"\n >\n 첨부 파일 삭제\n </Button>\n </Flex>\n </>\n );\n })}\n </Box>\n );\n }, [attachedFiles, fileDeleteLoading, handleDeleteFile]);\n\n const handleOnChangeTitle = useCallback<\n React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>\n >((e) => {\n setTitle(e.target.value);\n }, []);\n\n const clearEditorState = useCallback(() => {\n setContent('');\n setAttachedFiles([]);\n setTitle('');\n }, []);\n\n const handleSaveClick = useCallback(() => {\n onClickSave?.({\n title,\n content,\n files: attachedFiles,\n });\n }, [onClickSave, title, content, attachedFiles]);\n\n if (props.clearEditor) {\n props.clearEditor.current = clearEditorState;\n }\n\n return (\n <div className=\"tipp-ql-wrapper\">\n {/* 제목 입력창 */}\n <Grid\n align=\"center\"\n columns=\"auto auto 1fr\"\n gap=\"2\"\n height=\"42px\"\n pl=\"2\"\n pr=\"3\"\n width=\"100%\"\n >\n <Box pl=\"3\" pr=\"3\">\n <Typo>제목</Typo>\n </Box>\n <Separator orientation=\"vertical\" style={{ height: '100%' }} />\n <TextField.Root\n className=\"editor-title-text-field\"\n onChange={handleOnChangeTitle}\n placeholder=\"제목을 입력해주세요\"\n value={title}\n />\n </Grid>\n <ReactQuill\n className=\"tipp-ql-editor write-mode\"\n onChange={handleOnChangeContent}\n ref={editorRef}\n theme=\"snow\"\n value={content}\n {...quillProps}\n />\n {renderAttachedFiles()}\n <Separator size=\"4\" />\n <Flex align=\"center\" justify=\"between\" p=\"2\" pl=\"4\" pr=\"4\" width=\"100%\">\n <Button\n color=\"gray\"\n onClick={handleButtonClick}\n variant=\"transparent\"\n >\n <Link2Icon height={20} width={20} />\n </Button>\n\n <Flex gap=\"2\">\n {clearEditor ? (\n <Button color=\"gray\" onClick={clearEditorState} variant=\"outline\">\n 초기화\n </Button>\n ) : null}\n {SecondaryButton ? SecondaryButton : null}\n <Button disabled={isLoading} onClick={handleSaveClick}>\n 저장\n </Button>\n </Flex>\n </Flex>\n </div>\n );\n }\n);\n\nEditor.displayName = 'TIPP-Quill-Editor';\n"],"mappings":"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","names":["_a","ReactQuill"]}
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import ReactQuill, { ReactQuillProps } from 'react-quill';
3
+
4
+ interface Attachment {
5
+ fileName: string;
6
+ url: string;
7
+ }
8
+ interface TippEditorProps extends ReactQuillProps {
9
+ defaultTitle?: string;
10
+ defaultValue?: string;
11
+ defaultAttachedFiles?: Attachment[];
12
+ /** 저장하기 버튼 클릭 시 실행 */
13
+ onClickSave?: (values: {
14
+ title: string;
15
+ content: string;
16
+ files: Attachment[];
17
+ }) => void;
18
+ /** 파일 업로드 버튼 클릭 시 실행 */
19
+ uploadFile?: (file: File, destination: string) => Promise<Attachment | undefined>;
20
+ deleteFile?: (fileUrl: string) => Promise<void>;
21
+ /** 외부에서 Editor를 빈 상태로 초기화 시켜야 할 때 사용 */
22
+ clearEditor?: React.MutableRefObject<(() => void) | undefined>;
23
+ /** 초기화 버튼말고 다른 버튼 추가시 */
24
+ SecondaryButton?: React.ReactNode;
25
+ /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */
26
+ isLoading?: boolean;
27
+ }
28
+ declare const Editor: React.ForwardRefExoticComponent<TippEditorProps & React.RefAttributes<ReactQuill>>;
29
+
30
+ export { type Attachment, Editor, type TippEditorProps };
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import ReactQuill, { ReactQuillProps } from 'react-quill';
3
+
4
+ interface Attachment {
5
+ fileName: string;
6
+ url: string;
7
+ }
8
+ interface TippEditorProps extends ReactQuillProps {
9
+ defaultTitle?: string;
10
+ defaultValue?: string;
11
+ defaultAttachedFiles?: Attachment[];
12
+ /** 저장하기 버튼 클릭 시 실행 */
13
+ onClickSave?: (values: {
14
+ title: string;
15
+ content: string;
16
+ files: Attachment[];
17
+ }) => void;
18
+ /** 파일 업로드 버튼 클릭 시 실행 */
19
+ uploadFile?: (file: File, destination: string) => Promise<Attachment | undefined>;
20
+ deleteFile?: (fileUrl: string) => Promise<void>;
21
+ /** 외부에서 Editor를 빈 상태로 초기화 시켜야 할 때 사용 */
22
+ clearEditor?: React.MutableRefObject<(() => void) | undefined>;
23
+ /** 초기화 버튼말고 다른 버튼 추가시 */
24
+ SecondaryButton?: React.ReactNode;
25
+ /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */
26
+ isLoading?: boolean;
27
+ }
28
+ declare const Editor: React.ForwardRefExoticComponent<TippEditorProps & React.RefAttributes<ReactQuill>>;
29
+
30
+ export { Editor, type TippEditorProps };
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import ReactQuill, { ReactQuillProps } from 'react-quill';
3
+
4
+ interface Attachment {
5
+ fileName: string;
6
+ url: string;
7
+ }
8
+ interface TippEditorProps extends ReactQuillProps {
9
+ defaultTitle?: string;
10
+ defaultValue?: string;
11
+ defaultAttachedFiles?: Attachment[];
12
+ /** 저장하기 버튼 클릭 시 실행 */
13
+ onClickSave?: (values: {
14
+ title: string;
15
+ content: string;
16
+ files: Attachment[];
17
+ }) => void;
18
+ /** 파일 업로드 버튼 클릭 시 실행 */
19
+ uploadFile?: (file: File, destination: string) => Promise<Attachment | undefined>;
20
+ deleteFile?: (fileUrl: string) => Promise<void>;
21
+ /** 외부에서 Editor를 빈 상태로 초기화 시켜야 할 때 사용 */
22
+ clearEditor?: React.MutableRefObject<(() => void) | undefined>;
23
+ /** 초기화 버튼말고 다른 버튼 추가시 */
24
+ SecondaryButton?: React.ReactNode;
25
+ /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */
26
+ isLoading?: boolean;
27
+ }
28
+ declare const Editor: React.ForwardRefExoticComponent<TippEditorProps & React.RefAttributes<ReactQuill>>;
29
+
30
+ export { type Attachment, Editor, type TippEditorProps };