@tipp/ui-quill-editor 1.0.2 → 1.0.3

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.
@@ -0,0 +1,267 @@
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
+ minHeight,
80
+ maxHeight
81
+ } = _a, quillProps = __objRest(_a, [
82
+ "defaultAttachedFiles",
83
+ "defaultTitle",
84
+ "defaultValue",
85
+ "onClickSave",
86
+ "uploadFile",
87
+ "deleteFile",
88
+ "isLoading",
89
+ "SecondaryButton",
90
+ "clearEditor",
91
+ "minHeight",
92
+ "maxHeight"
93
+ ]);
94
+ const defaultRef = useRef(null);
95
+ const editorRef = ref || defaultRef;
96
+ const [attachedFiles, setAttachedFiles] = useState(
97
+ defaultAttachedFiles || []
98
+ );
99
+ const [fileDeleteLoading, setFileDeleteLoading] = useState(/* @__PURE__ */ new Set());
100
+ const [title, setTitle] = useState(defaultTitle || "");
101
+ const [content, setContent] = useState(defaultValue || "");
102
+ const handleOnChangeContent = useCallback((value) => {
103
+ setContent(value);
104
+ }, []);
105
+ const handleButtonClick = useCallback(() => {
106
+ let input = document.createElement("input");
107
+ input.type = "file";
108
+ input.onchange = (event) => __async(void 0, null, function* () {
109
+ var _a2;
110
+ const file = (_a2 = event.target.files) == null ? void 0 : _a2[0];
111
+ if (!file) {
112
+ toast.error("\uD30C\uC77C\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694.");
113
+ return;
114
+ }
115
+ const fileName = file.name;
116
+ const attachment = yield uploadFile == null ? void 0 : uploadFile(file, `hr-notes/${fileName}`);
117
+ if (attachment) {
118
+ setAttachedFiles((prev) => [...prev, attachment]);
119
+ }
120
+ input = null;
121
+ });
122
+ input.click();
123
+ }, [uploadFile]);
124
+ const handleDeleteFile = useCallback(
125
+ (fileUrl) => __async(void 0, null, function* () {
126
+ try {
127
+ setFileDeleteLoading((p) => p.add(fileUrl));
128
+ yield deleteFile == null ? void 0 : deleteFile(fileUrl);
129
+ setAttachedFiles(
130
+ (currentFiles) => currentFiles.filter((item) => item.url !== fileUrl)
131
+ );
132
+ } catch (err) {
133
+ toast.error("\uD30C\uC77C \uC0AD\uC81C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
134
+ } finally {
135
+ setFileDeleteLoading((p) => {
136
+ p.delete(fileUrl);
137
+ return p;
138
+ });
139
+ }
140
+ }),
141
+ [deleteFile]
142
+ );
143
+ const renderAttachedFiles = useCallback(() => {
144
+ return /* @__PURE__ */ jsx(Box, { width: "100%", children: attachedFiles.map((file) => {
145
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
146
+ /* @__PURE__ */ jsx(Separator, { size: "4" }),
147
+ /* @__PURE__ */ jsxs(
148
+ Flex,
149
+ {
150
+ align: "center",
151
+ justify: "between",
152
+ p: "4",
153
+ width: "100%",
154
+ children: [
155
+ /* @__PURE__ */ jsx(Link, { href: file.url, size: "2", children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "3", children: [
156
+ /* @__PURE__ */ jsx(FileIcon, {}),
157
+ file.fileName
158
+ ] }) }),
159
+ /* @__PURE__ */ jsx(
160
+ Button,
161
+ {
162
+ loading: fileDeleteLoading.has(file.url),
163
+ onClick: () => {
164
+ void handleDeleteFile(file.url);
165
+ },
166
+ variant: "ghost",
167
+ children: "\uCCA8\uBD80 \uD30C\uC77C \uC0AD\uC81C"
168
+ }
169
+ )
170
+ ]
171
+ },
172
+ `${file.url}_${file.fileName}`
173
+ )
174
+ ] });
175
+ }) });
176
+ }, [attachedFiles, fileDeleteLoading, handleDeleteFile]);
177
+ const handleOnChangeTitle = useCallback((e) => {
178
+ setTitle(e.target.value);
179
+ }, []);
180
+ const clearEditorState = useCallback(() => {
181
+ setContent("");
182
+ setAttachedFiles([]);
183
+ setTitle("");
184
+ }, []);
185
+ const handleSaveClick = useCallback(() => {
186
+ onClickSave == null ? void 0 : onClickSave({
187
+ title,
188
+ content,
189
+ files: attachedFiles
190
+ });
191
+ }, [onClickSave, title, content, attachedFiles]);
192
+ if (props.clearEditor) {
193
+ props.clearEditor.current = clearEditorState;
194
+ }
195
+ return /* @__PURE__ */ jsxs(
196
+ "div",
197
+ {
198
+ className: "tipp-ql-wrapper",
199
+ style: {
200
+ minHeight,
201
+ maxHeight
202
+ },
203
+ children: [
204
+ /* @__PURE__ */ jsxs(
205
+ Grid,
206
+ {
207
+ align: "center",
208
+ columns: "auto auto 1fr",
209
+ gap: "2",
210
+ height: "42px",
211
+ pl: "2",
212
+ pr: "3",
213
+ width: "100%",
214
+ children: [
215
+ /* @__PURE__ */ jsx(Box, { pl: "3", pr: "3", children: /* @__PURE__ */ jsx(Typo, { children: "\uC81C\uBAA9" }) }),
216
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", style: { height: "100%" } }),
217
+ /* @__PURE__ */ jsx(
218
+ TextField.Root,
219
+ {
220
+ className: "editor-title-text-field",
221
+ onChange: handleOnChangeTitle,
222
+ placeholder: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
223
+ value: title
224
+ }
225
+ )
226
+ ]
227
+ }
228
+ ),
229
+ /* @__PURE__ */ jsx(
230
+ ReactQuill,
231
+ __spreadValues({
232
+ className: "tipp-ql-editor write-mode",
233
+ onChange: handleOnChangeContent,
234
+ ref: editorRef,
235
+ theme: "snow",
236
+ value: content
237
+ }, quillProps)
238
+ ),
239
+ renderAttachedFiles(),
240
+ /* @__PURE__ */ jsx(Separator, { size: "4" }),
241
+ /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "between", p: "2", pl: "4", pr: "4", width: "100%", children: [
242
+ /* @__PURE__ */ jsx(
243
+ Button,
244
+ {
245
+ color: "gray",
246
+ onClick: handleButtonClick,
247
+ variant: "transparent",
248
+ children: /* @__PURE__ */ jsx(Link2Icon, { height: 20, width: 20 })
249
+ }
250
+ ),
251
+ /* @__PURE__ */ jsxs(Flex, { gap: "2", children: [
252
+ clearEditor ? /* @__PURE__ */ jsx(Button, { color: "gray", onClick: clearEditorState, variant: "outline", children: "\uCD08\uAE30\uD654" }) : null,
253
+ SecondaryButton ? SecondaryButton : null,
254
+ /* @__PURE__ */ jsx(Button, { disabled: isLoading, onClick: handleSaveClick, children: "\uC800\uC7A5" })
255
+ ] })
256
+ ] })
257
+ ]
258
+ }
259
+ );
260
+ }
261
+ );
262
+ Editor.displayName = "TIPP-Quill-Editor";
263
+
264
+ export {
265
+ Editor
266
+ };
267
+ //# sourceMappingURL=chunk-4ULZXT3Z.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 minHeight?: string;\n maxHeight?: string;\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 minHeight,\n maxHeight,\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\n className=\"tipp-ql-wrapper\"\n style={{\n minHeight,\n maxHeight,\n }}\n >\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAgB,YAAY,aAAa,QAAQ,gBAAgB;AACjE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,OAAO,gBAAgB;AA6GT,mBACE,KASI,YAVN;AA5EP,IAAM,SAAS;AAAA,EACpB,CAAC,OAAO,QAAyB;AAC/B,UAaI,YAZF;AAAA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IA7DN,IA+DQ,IADC,uBACD,IADC;AAAA,MAXH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGF,UAAM,aAAa,OAAmB,IAAI;AAC1C,UAAM,YAAY,OAAO;AACzB,UAAM,CAAC,eAAe,gBAAgB,IAAI;AAAA,MACxC,wBAAwB,CAAC;AAAA,IAC3B;AACA,UAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,oBAAI,IAAI,CAAC;AAEpE,UAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,gBAAgB,EAAE;AACrD,UAAM,CAAC,SAAS,UAAU,IAAI,SAAS,gBAAgB,EAAE;AAEzD,UAAM,wBAAwB,YAAY,CAAC,UAAkB;AAC3D,iBAAW,KAAK;AAAA,IAClB,GAAG,CAAC,CAAC;AAEL,UAAM,oBAAoB,YAAY,MAAM;AAC1C,UAAI,QAAiC,SAAS,cAAc,OAAO;AACnE,YAAM,OAAO;AACb,YAAM,WAAW,CAAO,UAAU;AAjFxC,YAAAA;AAkFQ,cAAM,QAAQA,MAAA,MAAM,OAA4B,UAAlC,gBAAAA,IAA0C;AACxD,YAAI,CAAC,MAAM;AAET,gBAAM,MAAM,0DAAa;AACzB;AAAA,QACF;AAEA,cAAM,WAAW,KAAK;AACtB,cAAM,aAAa,MAAM,yCAAa,MAAM,YAAY,QAAQ;AAChE,YAAI,YAAY;AACd,2BAAiB,CAAC,SAAS,CAAC,GAAG,MAAM,UAAU,CAAC;AAAA,QAClD;AACA,gBAAQ;AAAA,MACV;AACA,YAAM,MAAM;AAAA,IACd,GAAG,CAAC,UAAU,CAAC;AAEf,UAAM,mBAAmB;AAAA,MACvB,CAAO,YAAoB;AACzB,YAAI;AACF,+BAAqB,CAAC,MAAM,EAAE,IAAI,OAAO,CAAC;AAC1C,gBAAM,yCAAa;AACnB;AAAA,YAAiB,CAAC,iBAChB,aAAa,OAAO,CAAC,SAAS,KAAK,QAAQ,OAAO;AAAA,UACpD;AAAA,QACF,SAAS,KAAK;AACZ,gBAAM,MAAM,uEAAgB;AAAA,QAC9B,UAAE;AACA,+BAAqB,CAAC,MAAM;AAC1B,cAAE,OAAO,OAAO;AAChB,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA,CAAC,UAAU;AAAA,IACb;AAEA,UAAM,sBAAsB,YAAY,MAAM;AAC5C,aACE,oBAAC,OAAI,OAAM,QACR,wBAAc,IAAI,CAAC,SAAS;AAC3B,eACE,iCACE;AAAA,8BAAC,aAAU,MAAK,KAAI;AAAA,UACpB;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,SAAQ;AAAA,cAER,GAAE;AAAA,cACF,OAAM;AAAA,cAEN;AAAA,oCAAC,QAAK,MAAM,KAAK,KAAK,MAAK,KACzB,+BAAC,QAAK,OAAM,UAAS,KAAI,KACvB;AAAA,sCAAC,YAAS;AAAA,kBACT,KAAK;AAAA,mBACR,GACF;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAS,kBAAkB,IAAI,KAAK,GAAG;AAAA,oBACvC,SAAS,MAAM;AACb,2BAAK,iBAAiB,KAAK,GAAG;AAAA,oBAChC;AAAA,oBACA,SAAQ;AAAA,oBACT;AAAA;AAAA,gBAED;AAAA;AAAA;AAAA,YAlBK,GAAG,KAAK,GAAG,IAAI,KAAK,QAAQ;AAAA,UAmBnC;AAAA,WACF;AAAA,MAEJ,CAAC,GACH;AAAA,IAEJ,GAAG,CAAC,eAAe,mBAAmB,gBAAgB,CAAC;AAEvD,UAAM,sBAAsB,YAE1B,CAAC,MAAM;AACP,eAAS,EAAE,OAAO,KAAK;AAAA,IACzB,GAAG,CAAC,CAAC;AAEL,UAAM,mBAAmB,YAAY,MAAM;AACzC,iBAAW,EAAE;AACb,uBAAiB,CAAC,CAAC;AACnB,eAAS,EAAE;AAAA,IACb,GAAG,CAAC,CAAC;AAEL,UAAM,kBAAkB,YAAY,MAAM;AACxC,iDAAc;AAAA,QACZ;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT;AAAA,IACF,GAAG,CAAC,aAAa,OAAO,SAAS,aAAa,CAAC;AAE/C,QAAI,MAAM,aAAa;AACrB,YAAM,YAAY,UAAU;AAAA,IAC9B;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,QAGA;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,SAAQ;AAAA,cACR,KAAI;AAAA,cACJ,QAAO;AAAA,cACP,IAAG;AAAA,cACH,IAAG;AAAA,cACH,OAAM;AAAA,cAEN;AAAA,oCAAC,OAAI,IAAG,KAAI,IAAG,KACb,8BAAC,QAAK,0BAAE,GACV;AAAA,gBACA,oBAAC,aAAU,aAAY,YAAW,OAAO,EAAE,QAAQ,OAAO,GAAG;AAAA,gBAC7D;AAAA,kBAAC,UAAU;AAAA,kBAAV;AAAA,oBACC,WAAU;AAAA,oBACV,UAAU;AAAA,oBACV,aAAY;AAAA,oBACZ,OAAO;AAAA;AAAA,gBACT;AAAA;AAAA;AAAA,UACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,UAAU;AAAA,cACV,KAAK;AAAA,cACL,OAAM;AAAA,cACN,OAAO;AAAA,eACH;AAAA,UACN;AAAA,UACC,oBAAoB;AAAA,UACrB,oBAAC,aAAU,MAAK,KAAI;AAAA,UACpB,qBAAC,QAAK,OAAM,UAAS,SAAQ,WAAU,GAAE,KAAI,IAAG,KAAI,IAAG,KAAI,OAAM,QAC/D;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAM;AAAA,gBACN,SAAS;AAAA,gBACT,SAAQ;AAAA,gBAER,8BAAC,aAAU,QAAQ,IAAI,OAAO,IAAI;AAAA;AAAA,YACpC;AAAA,YAEA,qBAAC,QAAK,KAAI,KACP;AAAA,4BACC,oBAAC,UAAO,OAAM,QAAO,SAAS,kBAAkB,SAAQ,WAAU,gCAElE,IACE;AAAA,cACH,kBAAkB,kBAAkB;AAAA,cACrC,oBAAC,UAAO,UAAU,WAAW,SAAS,iBAAiB,0BAEvD;AAAA,eACF;AAAA,aACF;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":["_a"]}
package/dist/editor.cjs CHANGED
@@ -94,7 +94,9 @@ var Editor = (0, import_react.forwardRef)(
94
94
  deleteFile,
95
95
  isLoading,
96
96
  SecondaryButton,
97
- clearEditor
97
+ clearEditor,
98
+ minHeight,
99
+ maxHeight
98
100
  } = _a, quillProps = __objRest(_a, [
99
101
  "defaultAttachedFiles",
100
102
  "defaultTitle",
@@ -104,7 +106,9 @@ var Editor = (0, import_react.forwardRef)(
104
106
  "deleteFile",
105
107
  "isLoading",
106
108
  "SecondaryButton",
107
- "clearEditor"
109
+ "clearEditor",
110
+ "minHeight",
111
+ "maxHeight"
108
112
  ]);
109
113
  const defaultRef = (0, import_react.useRef)(null);
110
114
  const editorRef = ref || defaultRef;
@@ -207,61 +211,71 @@ var Editor = (0, import_react.forwardRef)(
207
211
  if (props.clearEditor) {
208
212
  props.clearEditor.current = clearEditorState;
209
213
  }
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%" } }),
214
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
215
+ "div",
216
+ {
217
+ className: "tipp-ql-wrapper",
218
+ style: {
219
+ minHeight,
220
+ maxHeight
221
+ },
222
+ children: [
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
224
+ import_ui.Grid,
225
+ {
226
+ align: "center",
227
+ columns: "auto auto 1fr",
228
+ gap: "2",
229
+ height: "42px",
230
+ pl: "2",
231
+ pr: "3",
232
+ width: "100%",
233
+ children: [
234
+ /* @__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" }) }),
235
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { orientation: "vertical", style: { height: "100%" } }),
236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
237
+ import_ui.TextField.Root,
238
+ {
239
+ className: "editor-title-text-field",
240
+ onChange: handleOnChangeTitle,
241
+ placeholder: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
242
+ value: title
243
+ }
244
+ )
245
+ ]
246
+ }
247
+ ),
248
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
249
+ import_react_quill.default,
250
+ __spreadValues({
251
+ className: "tipp-ql-editor write-mode",
252
+ onChange: handleOnChangeContent,
253
+ ref: editorRef,
254
+ theme: "snow",
255
+ value: content
256
+ }, quillProps)
257
+ ),
258
+ renderAttachedFiles(),
259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { size: "4" }),
260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { align: "center", justify: "between", p: "2", pl: "4", pr: "4", width: "100%", children: [
224
261
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
225
- import_ui.TextField.Root,
262
+ import_ui.Button,
226
263
  {
227
- className: "editor-title-text-field",
228
- onChange: handleOnChangeTitle,
229
- placeholder: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
230
- value: title
264
+ color: "gray",
265
+ onClick: handleButtonClick,
266
+ variant: "transparent",
267
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Link2Icon, { height: 20, width: 20 })
231
268
  }
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
- ] });
269
+ ),
270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { gap: "2", children: [
271
+ clearEditor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { color: "gray", onClick: clearEditorState, variant: "outline", children: "\uCD08\uAE30\uD654" }) : null,
272
+ SecondaryButton ? SecondaryButton : null,
273
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { disabled: isLoading, onClick: handleSaveClick, children: "\uC800\uC7A5" })
274
+ ] })
275
+ ] })
276
+ ]
277
+ }
278
+ );
265
279
  }
266
280
  );
267
281
  Editor.displayName = "TIPP-Quill-Editor";
@@ -1 +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"]}
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 minHeight?: string;\n maxHeight?: string;\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 minHeight,\n maxHeight,\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\n className=\"tipp-ql-wrapper\"\n style={{\n minHeight,\n maxHeight,\n }}\n >\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"]}
package/dist/editor.d.cts CHANGED
@@ -24,6 +24,8 @@ interface TippEditorProps extends ReactQuillProps {
24
24
  SecondaryButton?: React.ReactNode;
25
25
  /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */
26
26
  isLoading?: boolean;
27
+ minHeight?: string;
28
+ maxHeight?: string;
27
29
  }
28
30
  declare const Editor: React.ForwardRefExoticComponent<TippEditorProps & React.RefAttributes<ReactQuill>>;
29
31
 
package/dist/editor.d.ts CHANGED
@@ -24,6 +24,8 @@ interface TippEditorProps extends ReactQuillProps {
24
24
  SecondaryButton?: React.ReactNode;
25
25
  /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */
26
26
  isLoading?: boolean;
27
+ minHeight?: string;
28
+ maxHeight?: string;
27
29
  }
28
30
  declare const Editor: React.ForwardRefExoticComponent<TippEditorProps & React.RefAttributes<ReactQuill>>;
29
31
 
package/dist/editor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Editor
3
- } from "./chunk-ICAKLBT5.js";
3
+ } from "./chunk-4ULZXT3Z.js";
4
4
  export {
5
5
  Editor
6
6
  };
package/dist/index.cjs CHANGED
@@ -96,7 +96,9 @@ var Editor = (0, import_react.forwardRef)(
96
96
  deleteFile,
97
97
  isLoading,
98
98
  SecondaryButton,
99
- clearEditor
99
+ clearEditor,
100
+ minHeight,
101
+ maxHeight
100
102
  } = _a, quillProps = __objRest(_a, [
101
103
  "defaultAttachedFiles",
102
104
  "defaultTitle",
@@ -106,7 +108,9 @@ var Editor = (0, import_react.forwardRef)(
106
108
  "deleteFile",
107
109
  "isLoading",
108
110
  "SecondaryButton",
109
- "clearEditor"
111
+ "clearEditor",
112
+ "minHeight",
113
+ "maxHeight"
110
114
  ]);
111
115
  const defaultRef = (0, import_react.useRef)(null);
112
116
  const editorRef = ref || defaultRef;
@@ -209,61 +213,71 @@ var Editor = (0, import_react.forwardRef)(
209
213
  if (props.clearEditor) {
210
214
  props.clearEditor.current = clearEditorState;
211
215
  }
212
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tipp-ql-wrapper", children: [
213
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
214
- import_ui.Grid,
215
- {
216
- align: "center",
217
- columns: "auto auto 1fr",
218
- gap: "2",
219
- height: "42px",
220
- pl: "2",
221
- pr: "3",
222
- width: "100%",
223
- children: [
224
- /* @__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" }) }),
225
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { orientation: "vertical", style: { height: "100%" } }),
216
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
217
+ "div",
218
+ {
219
+ className: "tipp-ql-wrapper",
220
+ style: {
221
+ minHeight,
222
+ maxHeight
223
+ },
224
+ children: [
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
226
+ import_ui.Grid,
227
+ {
228
+ align: "center",
229
+ columns: "auto auto 1fr",
230
+ gap: "2",
231
+ height: "42px",
232
+ pl: "2",
233
+ pr: "3",
234
+ width: "100%",
235
+ children: [
236
+ /* @__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" }) }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { orientation: "vertical", style: { height: "100%" } }),
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
239
+ import_ui.TextField.Root,
240
+ {
241
+ className: "editor-title-text-field",
242
+ onChange: handleOnChangeTitle,
243
+ placeholder: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
244
+ value: title
245
+ }
246
+ )
247
+ ]
248
+ }
249
+ ),
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
251
+ import_react_quill.default,
252
+ __spreadValues({
253
+ className: "tipp-ql-editor write-mode",
254
+ onChange: handleOnChangeContent,
255
+ ref: editorRef,
256
+ theme: "snow",
257
+ value: content
258
+ }, quillProps)
259
+ ),
260
+ renderAttachedFiles(),
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { size: "4" }),
262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { align: "center", justify: "between", p: "2", pl: "4", pr: "4", width: "100%", children: [
226
263
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
227
- import_ui.TextField.Root,
264
+ import_ui.Button,
228
265
  {
229
- className: "editor-title-text-field",
230
- onChange: handleOnChangeTitle,
231
- placeholder: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694",
232
- value: title
266
+ color: "gray",
267
+ onClick: handleButtonClick,
268
+ variant: "transparent",
269
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Link2Icon, { height: 20, width: 20 })
233
270
  }
234
- )
235
- ]
236
- }
237
- ),
238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
239
- import_react_quill.default,
240
- __spreadValues({
241
- className: "tipp-ql-editor write-mode",
242
- onChange: handleOnChangeContent,
243
- ref: editorRef,
244
- theme: "snow",
245
- value: content
246
- }, quillProps)
247
- ),
248
- renderAttachedFiles(),
249
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Separator, { size: "4" }),
250
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { align: "center", justify: "between", p: "2", pl: "4", pr: "4", width: "100%", children: [
251
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
252
- import_ui.Button,
253
- {
254
- color: "gray",
255
- onClick: handleButtonClick,
256
- variant: "transparent",
257
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Link2Icon, { height: 20, width: 20 })
258
- }
259
- ),
260
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { gap: "2", children: [
261
- clearEditor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { color: "gray", onClick: clearEditorState, variant: "outline", children: "\uCD08\uAE30\uD654" }) : null,
262
- SecondaryButton ? SecondaryButton : null,
263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { disabled: isLoading, onClick: handleSaveClick, children: "\uC800\uC7A5" })
264
- ] })
265
- ] })
266
- ] });
271
+ ),
272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Flex, { gap: "2", children: [
273
+ clearEditor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { color: "gray", onClick: clearEditorState, variant: "outline", children: "\uCD08\uAE30\uD654" }) : null,
274
+ SecondaryButton ? SecondaryButton : null,
275
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Button, { disabled: isLoading, onClick: handleSaveClick, children: "\uC800\uC7A5" })
276
+ ] })
277
+ ] })
278
+ ]
279
+ }
280
+ );
267
281
  }
268
282
  );
269
283
  Editor.displayName = "TIPP-Quill-Editor";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/editor.tsx"],"sourcesContent":["export { Editor } from './editor';\n","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"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/editor.tsx"],"sourcesContent":["export { Editor } from './editor';\n","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 minHeight?: string;\n maxHeight?: string;\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 minHeight,\n maxHeight,\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\n className=\"tipp-ql-wrapper\"\n style={{\n minHeight,\n maxHeight,\n }}\n >\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"]}
package/dist/index.css CHANGED
@@ -925,8 +925,6 @@
925
925
  background: var(--color-panel);
926
926
  border: 1px solid var(--gray-7);
927
927
  --text-field-focus-color: transparent !important;
928
- display: grid;
929
- grid-template-rows: auto 1fr;
930
928
  }
931
929
  .tipp-ql-wrapper .editor-title-text-field {
932
930
  box-shadow: none;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Editor
3
- } from "./chunk-ICAKLBT5.js";
3
+ } from "./chunk-4ULZXT3Z.js";
4
4
  export {
5
5
  Editor
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tipp/ui-quill-editor",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "tipp 디자인 시스템이 적용된 quillEditor 패키지, quillEditor의 사이즈가 커서 별도 패키지로 분리했습니다.",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -46,8 +46,8 @@
46
46
  "postcss-nesting": "12.0.2",
47
47
  "tsup": "^8.0.2",
48
48
  "typescript": "^5.3.3",
49
- "@tipp/typescript-config": "1.0.0",
50
- "@tipp/eslint-config": "1.0.0"
49
+ "@tipp/eslint-config": "1.0.0",
50
+ "@tipp/typescript-config": "1.0.0"
51
51
  },
52
52
  "scripts": {
53
53
  "build": "pnpm run build:js && pnpm run build:css",
package/src/editor.tsx CHANGED
@@ -42,6 +42,8 @@ export interface TippEditorProps extends ReactQuillProps {
42
42
  SecondaryButton?: React.ReactNode;
43
43
  /** true인 경우 저장하기 버튼이 비활성 화 됨. 연타 방지 */
44
44
  isLoading?: boolean;
45
+ minHeight?: string;
46
+ maxHeight?: string;
45
47
  }
46
48
 
47
49
  export const Editor = forwardRef<ReactQuill, TippEditorProps>(
@@ -56,6 +58,8 @@ export const Editor = forwardRef<ReactQuill, TippEditorProps>(
56
58
  isLoading,
57
59
  SecondaryButton,
58
60
  clearEditor,
61
+ minHeight,
62
+ maxHeight,
59
63
  ...quillProps
60
64
  } = props;
61
65
  const defaultRef = useRef<ReactQuill>(null);
@@ -175,7 +179,13 @@ export const Editor = forwardRef<ReactQuill, TippEditorProps>(
175
179
  }
176
180
 
177
181
  return (
178
- <div className="tipp-ql-wrapper">
182
+ <div
183
+ className="tipp-ql-wrapper"
184
+ style={{
185
+ minHeight,
186
+ maxHeight,
187
+ }}
188
+ >
179
189
  {/* 제목 입력창 */}
180
190
  <Grid
181
191
  align="center"