@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 CHANGED
@@ -1,73 +1,82 @@
1
- # React + TypeScript + Vite
1
+ # @junbyeol/tiptap-editor
2
2
 
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
3
+ React용 Tiptap 리치 텍스트 에디터 컴포넌트입니다. 텍스트 서식, 표, 파일 첨부 등의 기능을 포함합니다.
4
4
 
5
- Currently, two official plugins are available:
5
+ ~**[데모 보기](https://your-demo-url.com)**~
6
+ 준비중
6
7
 
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
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
- ## React Compiler
10
+ - 텍스트 서식 (Bold, Italic, Strike, Underline, Code, 색상)
11
+ - 제목 (H1~H4), 목록, 텍스트 정렬
12
+ - 표 삽입 및 편집 (셀 병합, 배경색)
13
+ - 이미지 / 파일 드래그&드롭 및 붙여넣기
14
+ - 다크모드 지원
11
15
 
12
- The React Compiler is currently not compatible with SWC. See [this issue](https://github.com/vitejs/vite-plugin-react/issues/428) for tracking the progress.
16
+ ## 설치
13
17
 
14
- ## Expanding the ESLint configuration
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
- If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
33
+ ### Props
17
34
 
18
- ```js
19
- export default defineConfig([
20
- globalIgnores(['dist']),
21
- {
22
- files: ['**/*.{ts,tsx}'],
23
- extends: [
24
- // Other configs...
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
- // Remove tseslint.configs.recommended and replace with this
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
- // Other configs...
34
- ],
35
- languageOptions: {
36
- parserOptions: {
37
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
38
- tsconfigRootDir: import.meta.dirname,
39
- },
40
- // other options...
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
- You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
47
-
48
- ```js
49
- // eslint.config.js
50
- import reactX from 'eslint-plugin-react-x'
51
- import reactDom from 'eslint-plugin-react-dom'
52
-
53
- export default defineConfig([
54
- globalIgnores(['dist']),
55
- {
56
- files: ['**/*.{ts,tsx}'],
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?`![${a}](${l} "${c}")`:`![${a}](${l})`},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=({uploadFile:t,onFileInsert:e,onFileError:n,allowNonImageFile:r=!1,FileAttachmentComponent:o=P0})=>{const s=E.useRef(t),i=E.useRef(e),l=E.useRef(n),a=E.useRef(o);E.useEffect(()=>{s.current=t},[t]),E.useEffect(()=>{i.current=e},[e]),E.useEffect(()=>{l.current=n},[n]),E.useEffect(()=>{a.current=o},[o]);const c=E.useCallback(async h=>s.current?await s.current(h):await A5(h),[]),u=E.useCallback((h,p,m,g)=>{if(h){if(N5(p)){h.chain().insertContentAt(g,{type:"image",attrs:{src:m}}).focus().run();return}h.chain().insertContentAt(g,{type:"fileAttachment",attrs:{name:p.name,mimeType:p.type,url:m,size:p.size}}).focus().run()}},[]),d=Vw({extensions:[LS,R0,O0,N0,a5.configure({types:["heading","paragraph"]}),xm,T5,D0(a),w5.configure({table:{resizable:!0}}),I0.extend({addAttributes(){return{...this.parent?.(),backgroundColor:{default:null,renderHTML:h=>h.backgroundColor?{style:`background-color: ${h.backgroundColor}`}:{},parseHTML:h=>h.style.backgroundColor.replace(/['"]+/g,"")}}}}),E5.configure({allowedMimeTypes:r?void 0:R5,onDrop:async(h,p,m)=>{for(const g of p){const y=await c(g);i.current?.(g),u(h,g,y,m)}},onPaste:async(h,p,m)=>{if(m){l.current?.(new Error("외부에서 복사해온 파일을 바로 붙여넣기할 수 없습니다"));return}for(const g of p){const y=await c(g);i.current?.(g),u(h,g,y,h.state.selection.anchor)}}})],content:`
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?`![${a}](${l} "${c}")`:`![${a}](${l})`},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;