@junbyeol/tiptap-editor 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -60
- package/dist/index.cjs +1 -30
- package/dist/index.mjs +371 -393
- package/dist/types/tiptap/index.d.ts +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,73 +1,82 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @junbyeol/tiptap-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React용 Tiptap 리치 텍스트 에디터 컴포넌트입니다. 텍스트 서식, 표, 파일 첨부 등의 기능을 포함합니다.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
~**[데모 보기](https://your-demo-url.com)**~
|
|
6
|
+
준비중
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
|
8
|
+
## 기능
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
- 텍스트 서식 (Bold, Italic, Strike, Underline, Code, 색상)
|
|
11
|
+
- 제목 (H1~H4), 목록, 텍스트 정렬
|
|
12
|
+
- 표 삽입 및 편집 (셀 병합, 배경색)
|
|
13
|
+
- 이미지 / 파일 드래그&드롭 및 붙여넣기
|
|
14
|
+
- 다크모드 지원
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
## 설치
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
```bash
|
|
19
|
+
npm install @junbyeol/tiptap-editor
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 사용법
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { TiptapEditor } from "@junbyeol/tiptap-editor";
|
|
26
|
+
import "@junbyeol/tiptap-editor/style.css";
|
|
27
|
+
|
|
28
|
+
export default function App() {
|
|
29
|
+
return <TiptapEditor />;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
15
32
|
|
|
16
|
-
|
|
33
|
+
### Props
|
|
17
34
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
| Prop | 타입 | 설명 |
|
|
36
|
+
| ------------------------- | ----------------------------------------- | ----------------------------------------------------------------------- |
|
|
37
|
+
| `defaultValue` | `string` | 초기 HTML 콘텐츠. 마운트 시에만 적용됨 |
|
|
38
|
+
| `onChange` | `(html: string) => void` | 콘텐츠 변경 시 호출. HTML 문자열을 반환 |
|
|
39
|
+
| `uploadFile` | `(file: File) => Promise<string>` | 파일을 업로드하고 URL을 반환. 미제공 시 base64로 브라우저 메모리에 저장 |
|
|
40
|
+
| `onFileInsert` | `(file: File) => void` | 파일이 에디터에 삽입될 때 호출 |
|
|
41
|
+
| `onFileError` | `(error: Error) => void` | 파일 처리 오류 시 호출 |
|
|
42
|
+
| `allowNonImageFile` | `boolean` | 이미지 외 파일(PDF 등) 허용 여부 (기본값: `false`) |
|
|
43
|
+
| `FileAttachmentComponent` | `ComponentType<FileAttachmentAttributes>` | 비이미지 파일을 렌더링할 컴포넌트 |
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
tseslint.configs.recommendedTypeChecked,
|
|
28
|
-
// Alternatively, use this for stricter rules
|
|
29
|
-
tseslint.configs.strictTypeChecked,
|
|
30
|
-
// Optionally, add this for stylistic rules
|
|
31
|
-
tseslint.configs.stylisticTypeChecked,
|
|
45
|
+
### 파일 업로드 예시
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
```tsx
|
|
48
|
+
import { TiptapEditor } from "@junbyeol/tiptap-editor";
|
|
49
|
+
import "@junbyeol/tiptap-editor/style.css";
|
|
50
|
+
|
|
51
|
+
const uploadFile = async (file: File): Promise<string> => {
|
|
52
|
+
const formData = new FormData();
|
|
53
|
+
formData.append("file", file);
|
|
54
|
+
const res = await fetch("/api/upload", { method: "POST", body: formData });
|
|
55
|
+
const { url } = await res.json();
|
|
56
|
+
return url;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default function App() {
|
|
60
|
+
return (
|
|
61
|
+
<TiptapEditor
|
|
62
|
+
uploadFile={uploadFile}
|
|
63
|
+
onFileInsert={(file) => console.log("삽입됨:", file.name)}
|
|
64
|
+
onFileError={(error) => console.error(error)}
|
|
65
|
+
allowNonImageFile
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
44
69
|
```
|
|
45
70
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
extends: [
|
|
58
|
-
// Other configs...
|
|
59
|
-
// Enable lint rules for React
|
|
60
|
-
reactX.configs['recommended-typescript'],
|
|
61
|
-
// Enable lint rules for React DOM
|
|
62
|
-
reactDom.configs.recommended,
|
|
63
|
-
],
|
|
64
|
-
languageOptions: {
|
|
65
|
-
parserOptions: {
|
|
66
|
-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
67
|
-
tsconfigRootDir: import.meta.dirname,
|
|
68
|
-
},
|
|
69
|
-
// other options...
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
])
|
|
71
|
+
## 로컬 개발
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 의존성 설치
|
|
75
|
+
yarn
|
|
76
|
+
|
|
77
|
+
# 데모 앱 실행
|
|
78
|
+
yarn dev
|
|
79
|
+
|
|
80
|
+
# 라이브러리 빌드
|
|
81
|
+
yarn build:lib
|
|
73
82
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -183,33 +183,4 @@ ${h.slice(c+2)}`,d+=1;else break}e.push({indent:c,number:parseInt(l,10),content:
|
|
|
183
183
|
`;const f=new Array(i).fill(0).map((p,m)=>u&&c[m]&&c[m].text||"");return d+=`| ${f.map((p,m)=>a(p,l[m])).join(" | ")} |
|
|
184
184
|
`,d+=`| ${l.map(p=>"-".repeat(Math.max(3,p))).join(" | ")} |
|
|
185
185
|
`,(u?s.slice(1):s).forEach(p=>{d+=`| ${new Array(i).fill(0).map((m,g)=>a(p[g]&&p[g].text||"",l[g])).join(" | ")} |
|
|
186
|
-
`}),d}var v5=b5,C5=ge.create({name:"table",addOptions(){return{HTMLAttributes:{},resizable:!1,renderWrapper:!1,handleWidth:5,cellMinWidth:25,View:d5,lastColumnResizable:!0,allowTableNodeSelection:!1}},content:"tableRow+",tableRole:"table",isolating:!0,group:"block",parseHTML(){return[{tag:"table"}]},renderHTML({node:t,HTMLAttributes:e}){const{colgroup:n,tableWidth:r,tableMinWidth:o}=f5(t,this.options.cellMinWidth),s=e.style;function i(){return s||(r?`width: ${r}`:`min-width: ${o}`)}const l=["table",Q(this.options.HTMLAttributes,e,{style:i()}),n,["tbody",0]];return this.options.renderWrapper?["div",{class:"tableWrapper"},l]:l},parseMarkdown:(t,e)=>{const n=[];if(t.header){const r=[];t.header.forEach(o=>{r.push(e.createNode("tableHeader",{},[{type:"paragraph",content:e.parseInline(o.tokens)}]))}),n.push(e.createNode("tableRow",{},r))}return t.rows&&t.rows.forEach(r=>{const o=[];r.forEach(s=>{o.push(e.createNode("tableCell",{},[{type:"paragraph",content:e.parseInline(s.tokens)}]))}),n.push(e.createNode("tableRow",{},o))}),e.createNode("table",void 0,n)},renderMarkdown:(t,e)=>v5(t,e),addCommands(){return{insertTable:({rows:t=3,cols:e=3,withHeaderRow:n=!0}={})=>({tr:r,dispatch:o,editor:s})=>{const i=p5(s.schema,t,e,n);if(o){const l=r.selection.from+1;r.replaceSelectionWith(i).scrollIntoView().setSelection(B.near(r.doc.resolve(l)))}return!0},addColumnBefore:()=>({state:t,dispatch:e})=>YS(t,e),addColumnAfter:()=>({state:t,dispatch:e})=>XS(t,e),deleteColumn:()=>({state:t,dispatch:e})=>eE(t,e),addRowBefore:()=>({state:t,dispatch:e})=>nE(t,e),addRowAfter:()=>({state:t,dispatch:e})=>rE(t,e),deleteRow:()=>({state:t,dispatch:e})=>sE(t,e),deleteTable:()=>({state:t,dispatch:e})=>fE(t,e),mergeCells:()=>({state:t,dispatch:e})=>_d(t,e),splitCell:()=>({state:t,dispatch:e})=>Hd(t,e),toggleHeaderColumn:()=>({state:t,dispatch:e})=>Kr("column")(t,e),toggleHeaderRow:()=>({state:t,dispatch:e})=>Kr("row")(t,e),toggleHeaderCell:()=>({state:t,dispatch:e})=>uE(t,e),mergeOrSplit:()=>({state:t,dispatch:e})=>_d(t,e)?!0:Hd(t,e),setCellAttribute:(t,e)=>({state:n,dispatch:r})=>aE(t,e)(n,r),goToNextCell:()=>({state:t,dispatch:e})=>Bd(1)(t,e),goToPreviousCell:()=>({state:t,dispatch:e})=>Bd(-1)(t,e),fixTables:()=>({state:t,dispatch:e})=>(e&&Tm(t),!0),setCellSelection:t=>({tr:e,dispatch:n})=>{if(n){const r=ne.create(e.doc,t.anchorCell,t.headCell);e.setSelection(r)}return!0}}},addKeyboardShortcuts(){return{Tab:()=>this.editor.commands.goToNextCell()?!0:this.editor.can().addRowAfter()?this.editor.chain().addRowAfter().goToNextCell().run():!1,"Shift-Tab":()=>this.editor.commands.goToPreviousCell(),Backspace:Lo,"Mod-Backspace":Lo,Delete:Lo,"Mod-Delete":Lo}},addProseMirrorPlugins(){return[...this.options.resizable&&this.editor.isEditable?[xE({handleWidth:this.options.handleWidth,cellMinWidth:this.options.cellMinWidth,defaultCellMinWidth:this.options.cellMinWidth,View:this.options.View,lastColumnResizable:this.options.lastColumnResizable})]:[],IE({allowTableNodeSelection:this.options.allowTableNodeSelection})]},extendNodeSchema(t){const e={name:t.name,options:t.options,storage:t.storage};return{tableRole:Z(z(t,"tableRole",e))}}}),w5=Y.create({name:"tableKit",addExtensions(){const t=[];return this.options.table!==!1&&t.push(C5.configure(this.options.table)),this.options.tableCell!==!1&&t.push(I0.configure(this.options.tableCell)),this.options.tableHeader!==!1&&t.push(c5.configure(this.options.tableHeader)),this.options.tableRow!==!1&&t.push(u5.configure(this.options.tableRow)),t}}),x5=({key:t,editor:e,onPaste:n,onDrop:r,allowedMimeTypes:o})=>new re({key:t||new ae("fileHandler"),props:{handleDrop(s,i){var l;if(!r||!((l=i.dataTransfer)!=null&&l.files.length))return!1;const a=s.posAtCoords({left:i.clientX,top:i.clientY});let c=Array.from(i.dataTransfer.files);return o&&(c=c.filter(u=>o.includes(u.type))),c.length===0?!1:(i.preventDefault(),i.stopPropagation(),r(e,c,a?.pos||0),!0)},handlePaste(s,i){var l;if(!n||!((l=i.clipboardData)!=null&&l.files.length))return!1;let a=Array.from(i.clipboardData.files);const c=i.clipboardData.getData("text/html");return o&&(a=a.filter(u=>o.includes(u.type))),!(a.length===0||(i.preventDefault(),i.stopPropagation(),n(e,a,c),c.length>0))}}}),S5=Y.create({name:"fileHandler",addOptions(){return{onPaste:void 0,onDrop:void 0,allowedMimeTypes:void 0}},addProseMirrorPlugins(){return[x5({key:new ae(this.name),editor:this.editor,allowedMimeTypes:this.options.allowedMimeTypes,onDrop:this.options.onDrop,onPaste:this.options.onPaste})]}}),E5=S5,k5=/(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/,M5=ge.create({name:"image",addOptions(){return{inline:!1,allowBase64:!1,HTMLAttributes:{},resize:!1}},inline(){return this.options.inline},group(){return this.options.inline?"inline":"block"},draggable:!0,addAttributes(){return{src:{default:null},alt:{default:null},title:{default:null},width:{default:null},height:{default:null}}},parseHTML(){return[{tag:this.options.allowBase64?"img[src]":'img[src]:not([src^="data:"])'}]},renderHTML({HTMLAttributes:t}){return["img",Q(this.options.HTMLAttributes,t)]},parseMarkdown:(t,e)=>e.createNode("image",{src:t.href,title:t.title,alt:t.text}),renderMarkdown:t=>{var e,n,r,o,s,i;const l=(n=(e=t.attrs)==null?void 0:e.src)!=null?n:"",a=(o=(r=t.attrs)==null?void 0:r.alt)!=null?o:"",c=(i=(s=t.attrs)==null?void 0:s.title)!=null?i:"";return c?``:``},addNodeView(){if(!this.options.resize||!this.options.resize.enabled||typeof document>"u")return null;const{directions:t,minWidth:e,minHeight:n,alwaysPreserveAspectRatio:r}=this.options.resize;return({node:o,getPos:s,HTMLAttributes:i,editor:l})=>{const a=document.createElement("img");Object.entries(i).forEach(([d,f])=>{if(f!=null)switch(d){case"width":case"height":break;default:a.setAttribute(d,f);break}}),a.src=i.src;const c=new L2({element:a,editor:l,node:o,getPos:s,onResize:(d,f)=>{a.style.width=`${d}px`,a.style.height=`${f}px`},onCommit:(d,f)=>{const h=s();h!==void 0&&this.editor.chain().setNodeSelection(h).updateAttributes(this.name,{width:d,height:f}).run()},onUpdate:(d,f,h)=>d.type===o.type,options:{directions:t,min:{width:e,height:n},preserveAspectRatio:r===!0}}),u=c.dom;return u.style.visibility="hidden",u.style.pointerEvents="none",a.onload=()=>{u.style.visibility="",u.style.pointerEvents=""},c}},addCommands(){return{setImage:t=>({commands:e})=>e.insertContent({type:this.name,attrs:t})}},addInputRules(){return[Vp({find:k5,type:this.type,getAttributes:t=>{const[,,e,n,r]=t;return{src:n,alt:e,title:r}}})]}}),T5=M5;const D0=t=>{const e=({node:n})=>{const r=t.current;return r?b.jsx(Kw,{children:b.jsx(r,{...n.attrs})}):null};return ge.create({name:"fileAttachment",group:"block",atom:!0,addAttributes(){return{name:{default:""},mimeType:{default:""},url:{default:""},size:{default:0}}},parseHTML(){return[{tag:'div[data-type="file-attachment"]'}]},renderHTML({HTMLAttributes:n}){return["div",Q(n,{"data-type":"file-attachment"})]},addNodeView(){return Xw(e)}})},P0=({name:t,url:e})=>b.jsxs("a",{href:e,download:t,className:"file-attachment",tabIndex:0,"aria-label":`${t} 다운로드`,children:[b.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",className:"file-attachment__icon","aria-hidden":"true",children:b.jsx("path",{d:"M12 5v14M5 12l7 7 7-7"})}),b.jsx("span",{className:"file-attachment__name",children:t})]}),A5=t=>new Promise((e,n)=>{const r=new FileReader;r.onload=()=>e(r.result),r.onerror=n,r.readAsDataURL(t)}),R5=["image/png","image/jpeg","image/gif","image/webp"],N5=t=>t.type.startsWith("image/"),O5=({
|
|
187
|
-
<h2>
|
|
188
|
-
Hi there,
|
|
189
|
-
</h2>
|
|
190
|
-
<p>
|
|
191
|
-
this is a <em>basic</em> example of <strong>Tiptap</strong>. Sure, there are all kind of basic text styles you'd probably expect from a text editor. But wait until you see the lists:
|
|
192
|
-
</p>
|
|
193
|
-
<ul>
|
|
194
|
-
<li>
|
|
195
|
-
That's a bullet list with one …
|
|
196
|
-
</li>
|
|
197
|
-
<li>
|
|
198
|
-
… or two list items.
|
|
199
|
-
</li>
|
|
200
|
-
</ul>
|
|
201
|
-
<p>
|
|
202
|
-
Isn't that great? And all of that is editable. B ut wait, there's more. Let's try a code block:
|
|
203
|
-
</p>
|
|
204
|
-
<pre><code class="language-css">body {
|
|
205
|
-
display: none;
|
|
206
|
-
}</code></pre>
|
|
207
|
-
<p>
|
|
208
|
-
I know, I know, this is impressive. It's only the tip of the iceberg though. Give it a try and click a little bit around. Don't forget to check the other examples too.
|
|
209
|
-
</p>
|
|
210
|
-
<blockquote>
|
|
211
|
-
Wow, that's amazing. Good work, boy! 👏
|
|
212
|
-
<br />
|
|
213
|
-
— Mom
|
|
214
|
-
</blockquote>
|
|
215
|
-
`}),f=E.useMemo(()=>({editor:d}),[d]);return b.jsx(I5,{children:b.jsxs(li.Provider,{value:f,children:[b.jsx(t5,{editor:d}),b.jsx(Kp,{editor:d})]})})},I5=({children:t})=>b.jsx("div",{className:"tiptap-wrapper",children:t});exports.DefaultFileAttachmentComponent=P0;exports.TiptapEditor=O5;exports.createFileAttachmentExtension=D0;
|
|
186
|
+
`}),d}var v5=b5,C5=ge.create({name:"table",addOptions(){return{HTMLAttributes:{},resizable:!1,renderWrapper:!1,handleWidth:5,cellMinWidth:25,View:d5,lastColumnResizable:!0,allowTableNodeSelection:!1}},content:"tableRow+",tableRole:"table",isolating:!0,group:"block",parseHTML(){return[{tag:"table"}]},renderHTML({node:t,HTMLAttributes:e}){const{colgroup:n,tableWidth:r,tableMinWidth:o}=f5(t,this.options.cellMinWidth),s=e.style;function i(){return s||(r?`width: ${r}`:`min-width: ${o}`)}const l=["table",Q(this.options.HTMLAttributes,e,{style:i()}),n,["tbody",0]];return this.options.renderWrapper?["div",{class:"tableWrapper"},l]:l},parseMarkdown:(t,e)=>{const n=[];if(t.header){const r=[];t.header.forEach(o=>{r.push(e.createNode("tableHeader",{},[{type:"paragraph",content:e.parseInline(o.tokens)}]))}),n.push(e.createNode("tableRow",{},r))}return t.rows&&t.rows.forEach(r=>{const o=[];r.forEach(s=>{o.push(e.createNode("tableCell",{},[{type:"paragraph",content:e.parseInline(s.tokens)}]))}),n.push(e.createNode("tableRow",{},o))}),e.createNode("table",void 0,n)},renderMarkdown:(t,e)=>v5(t,e),addCommands(){return{insertTable:({rows:t=3,cols:e=3,withHeaderRow:n=!0}={})=>({tr:r,dispatch:o,editor:s})=>{const i=p5(s.schema,t,e,n);if(o){const l=r.selection.from+1;r.replaceSelectionWith(i).scrollIntoView().setSelection(B.near(r.doc.resolve(l)))}return!0},addColumnBefore:()=>({state:t,dispatch:e})=>YS(t,e),addColumnAfter:()=>({state:t,dispatch:e})=>XS(t,e),deleteColumn:()=>({state:t,dispatch:e})=>eE(t,e),addRowBefore:()=>({state:t,dispatch:e})=>nE(t,e),addRowAfter:()=>({state:t,dispatch:e})=>rE(t,e),deleteRow:()=>({state:t,dispatch:e})=>sE(t,e),deleteTable:()=>({state:t,dispatch:e})=>fE(t,e),mergeCells:()=>({state:t,dispatch:e})=>_d(t,e),splitCell:()=>({state:t,dispatch:e})=>Hd(t,e),toggleHeaderColumn:()=>({state:t,dispatch:e})=>Kr("column")(t,e),toggleHeaderRow:()=>({state:t,dispatch:e})=>Kr("row")(t,e),toggleHeaderCell:()=>({state:t,dispatch:e})=>uE(t,e),mergeOrSplit:()=>({state:t,dispatch:e})=>_d(t,e)?!0:Hd(t,e),setCellAttribute:(t,e)=>({state:n,dispatch:r})=>aE(t,e)(n,r),goToNextCell:()=>({state:t,dispatch:e})=>Bd(1)(t,e),goToPreviousCell:()=>({state:t,dispatch:e})=>Bd(-1)(t,e),fixTables:()=>({state:t,dispatch:e})=>(e&&Tm(t),!0),setCellSelection:t=>({tr:e,dispatch:n})=>{if(n){const r=ne.create(e.doc,t.anchorCell,t.headCell);e.setSelection(r)}return!0}}},addKeyboardShortcuts(){return{Tab:()=>this.editor.commands.goToNextCell()?!0:this.editor.can().addRowAfter()?this.editor.chain().addRowAfter().goToNextCell().run():!1,"Shift-Tab":()=>this.editor.commands.goToPreviousCell(),Backspace:Lo,"Mod-Backspace":Lo,Delete:Lo,"Mod-Delete":Lo}},addProseMirrorPlugins(){return[...this.options.resizable&&this.editor.isEditable?[xE({handleWidth:this.options.handleWidth,cellMinWidth:this.options.cellMinWidth,defaultCellMinWidth:this.options.cellMinWidth,View:this.options.View,lastColumnResizable:this.options.lastColumnResizable})]:[],IE({allowTableNodeSelection:this.options.allowTableNodeSelection})]},extendNodeSchema(t){const e={name:t.name,options:t.options,storage:t.storage};return{tableRole:Z(z(t,"tableRole",e))}}}),w5=Y.create({name:"tableKit",addExtensions(){const t=[];return this.options.table!==!1&&t.push(C5.configure(this.options.table)),this.options.tableCell!==!1&&t.push(I0.configure(this.options.tableCell)),this.options.tableHeader!==!1&&t.push(c5.configure(this.options.tableHeader)),this.options.tableRow!==!1&&t.push(u5.configure(this.options.tableRow)),t}}),x5=({key:t,editor:e,onPaste:n,onDrop:r,allowedMimeTypes:o})=>new re({key:t||new ae("fileHandler"),props:{handleDrop(s,i){var l;if(!r||!((l=i.dataTransfer)!=null&&l.files.length))return!1;const a=s.posAtCoords({left:i.clientX,top:i.clientY});let c=Array.from(i.dataTransfer.files);return o&&(c=c.filter(u=>o.includes(u.type))),c.length===0?!1:(i.preventDefault(),i.stopPropagation(),r(e,c,a?.pos||0),!0)},handlePaste(s,i){var l;if(!n||!((l=i.clipboardData)!=null&&l.files.length))return!1;let a=Array.from(i.clipboardData.files);const c=i.clipboardData.getData("text/html");return o&&(a=a.filter(u=>o.includes(u.type))),!(a.length===0||(i.preventDefault(),i.stopPropagation(),n(e,a,c),c.length>0))}}}),S5=Y.create({name:"fileHandler",addOptions(){return{onPaste:void 0,onDrop:void 0,allowedMimeTypes:void 0}},addProseMirrorPlugins(){return[x5({key:new ae(this.name),editor:this.editor,allowedMimeTypes:this.options.allowedMimeTypes,onDrop:this.options.onDrop,onPaste:this.options.onPaste})]}}),E5=S5,k5=/(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/,M5=ge.create({name:"image",addOptions(){return{inline:!1,allowBase64:!1,HTMLAttributes:{},resize:!1}},inline(){return this.options.inline},group(){return this.options.inline?"inline":"block"},draggable:!0,addAttributes(){return{src:{default:null},alt:{default:null},title:{default:null},width:{default:null},height:{default:null}}},parseHTML(){return[{tag:this.options.allowBase64?"img[src]":'img[src]:not([src^="data:"])'}]},renderHTML({HTMLAttributes:t}){return["img",Q(this.options.HTMLAttributes,t)]},parseMarkdown:(t,e)=>e.createNode("image",{src:t.href,title:t.title,alt:t.text}),renderMarkdown:t=>{var e,n,r,o,s,i;const l=(n=(e=t.attrs)==null?void 0:e.src)!=null?n:"",a=(o=(r=t.attrs)==null?void 0:r.alt)!=null?o:"",c=(i=(s=t.attrs)==null?void 0:s.title)!=null?i:"";return c?``:``},addNodeView(){if(!this.options.resize||!this.options.resize.enabled||typeof document>"u")return null;const{directions:t,minWidth:e,minHeight:n,alwaysPreserveAspectRatio:r}=this.options.resize;return({node:o,getPos:s,HTMLAttributes:i,editor:l})=>{const a=document.createElement("img");Object.entries(i).forEach(([d,f])=>{if(f!=null)switch(d){case"width":case"height":break;default:a.setAttribute(d,f);break}}),a.src=i.src;const c=new L2({element:a,editor:l,node:o,getPos:s,onResize:(d,f)=>{a.style.width=`${d}px`,a.style.height=`${f}px`},onCommit:(d,f)=>{const h=s();h!==void 0&&this.editor.chain().setNodeSelection(h).updateAttributes(this.name,{width:d,height:f}).run()},onUpdate:(d,f,h)=>d.type===o.type,options:{directions:t,min:{width:e,height:n},preserveAspectRatio:r===!0}}),u=c.dom;return u.style.visibility="hidden",u.style.pointerEvents="none",a.onload=()=>{u.style.visibility="",u.style.pointerEvents=""},c}},addCommands(){return{setImage:t=>({commands:e})=>e.insertContent({type:this.name,attrs:t})}},addInputRules(){return[Vp({find:k5,type:this.type,getAttributes:t=>{const[,,e,n,r]=t;return{src:n,alt:e,title:r}}})]}}),T5=M5;const D0=t=>{const e=({node:n})=>{const r=t.current;return r?b.jsx(Kw,{children:b.jsx(r,{...n.attrs})}):null};return ge.create({name:"fileAttachment",group:"block",atom:!0,addAttributes(){return{name:{default:""},mimeType:{default:""},url:{default:""},size:{default:0}}},parseHTML(){return[{tag:'div[data-type="file-attachment"]'}]},renderHTML({HTMLAttributes:n}){return["div",Q(n,{"data-type":"file-attachment"})]},addNodeView(){return Xw(e)}})},P0=({name:t,url:e})=>b.jsxs("a",{href:e,download:t,className:"file-attachment",tabIndex:0,"aria-label":`${t} 다운로드`,children:[b.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",className:"file-attachment__icon","aria-hidden":"true",children:b.jsx("path",{d:"M12 5v14M5 12l7 7 7-7"})}),b.jsx("span",{className:"file-attachment__name",children:t})]}),A5=t=>new Promise((e,n)=>{const r=new FileReader;r.onload=()=>e(r.result),r.onerror=n,r.readAsDataURL(t)}),R5=["image/png","image/jpeg","image/gif","image/webp"],N5=t=>t.type.startsWith("image/"),O5=({defaultValue:t="",onChange:e,uploadFile:n,onFileInsert:r,onFileError:o,allowNonImageFile:s=!1,FileAttachmentComponent:i=P0})=>{const l=E.useRef(e),a=E.useRef(n),c=E.useRef(r),u=E.useRef(o),d=E.useRef(i);E.useEffect(()=>{l.current=e},[e]),E.useEffect(()=>{a.current=n},[n]),E.useEffect(()=>{c.current=r},[r]),E.useEffect(()=>{u.current=o},[o]),E.useEffect(()=>{d.current=i},[i]);const f=E.useCallback(async g=>a.current?await a.current(g):await A5(g),[]),h=E.useCallback((g,y,C,w)=>{if(g){if(N5(y)){g.chain().insertContentAt(w,{type:"image",attrs:{src:C}}).focus().run();return}g.chain().insertContentAt(w,{type:"fileAttachment",attrs:{name:y.name,mimeType:y.type,url:C,size:y.size}}).focus().run()}},[]),p=Vw({extensions:[LS,R0,O0,N0,a5.configure({types:["heading","paragraph"]}),xm,T5,D0(d),w5.configure({table:{resizable:!0}}),I0.extend({addAttributes(){return{...this.parent?.(),backgroundColor:{default:null,renderHTML:g=>g.backgroundColor?{style:`background-color: ${g.backgroundColor}`}:{},parseHTML:g=>g.style.backgroundColor.replace(/['"]+/g,"")}}}}),E5.configure({allowedMimeTypes:s?void 0:R5,onDrop:async(g,y,C)=>{for(const w of y){const x=await f(w);c.current?.(w),h(g,w,x,C)}},onPaste:async(g,y,C)=>{if(C){u.current?.(new Error("외부에서 복사해온 파일을 바로 붙여넣기할 수 없습니다"));return}for(const w of y){const x=await f(w);c.current?.(w),h(g,w,x,g.state.selection.anchor)}}})],content:t,onUpdate:({editor:g})=>{l.current?.(g.getHTML())}}),m=E.useMemo(()=>({editor:p}),[p]);return b.jsx(I5,{children:b.jsxs(li.Provider,{value:m,children:[b.jsx(t5,{editor:p}),b.jsx(Kp,{editor:p})]})})},I5=({children:t})=>b.jsx("div",{className:"tiptap-wrapper",children:t});exports.DefaultFileAttachmentComponent=P0;exports.TiptapEditor=O5;exports.createFileAttachmentExtension=D0;
|