vue-flexible-text-editor 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Vue 3 + Vite
2
+
3
+ This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4
+
5
+ Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
package/dist/vite.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
@@ -0,0 +1,2 @@
1
+ .custom-editor-wrapper[data-v-222aa227]{background:#fff;border:2px solid #2c3e50;border-radius:12px;font-family:Segoe UI,sans-serif;overflow:hidden}.toolbar[data-v-222aa227]{background:#f8f9fa;border-bottom:2px solid #eee;flex-wrap:wrap;align-items:center;gap:10px;padding:12px;display:flex}.group[data-v-222aa227]{align-items:center;gap:5px;display:flex}.toolbar button[data-v-222aa227]{cursor:pointer;background:#fff;border:1px solid #ddd;border-radius:4px;padding:6px 10px;font-weight:600;transition:all .2s}.toolbar button[data-v-222aa227]:hover{background:#e9ecef}.preview-btn[data-v-222aa227]{color:#fff!important;background:#27ae60!important;border:none!important}.export-btn.pdf[data-v-222aa227]{color:#fff!important;background:#e74c3c!important;border:none!important}.export-btn.word[data-v-222aa227]{color:#fff!important;background:#2980b9!important;border:none!important}.toolbar button.active[data-v-222aa227]{color:#fff;background:#3498db;border-color:#2980b9}.font-select[data-v-222aa227],.size-select[data-v-222aa227]{border:1px solid #ddd;border-radius:4px;padding:5px}.color-picker[data-v-222aa227]{cursor:pointer;border:none;width:30px;height:30px;padding:0}.divider[data-v-222aa227]{background:#ddd;width:1px;height:30px}.editor-container[data-v-222aa227]{position:relative}.editor-area[data-v-222aa227]{outline:none;min-height:400px;padding:30px;font-size:16px;line-height:1.6}[data-v-222aa227] .resizable-media{cursor:pointer;vertical-align:top;border:2px solid #0000;margin:10px 5px;display:inline-block}[data-v-222aa227] table.resizable-media{border-collapse:collapse}[data-v-222aa227] .resizable-media:hover{border-color:#3498db}.resizer-overlay[data-v-222aa227]{pointer-events:none;z-index:10;border:2px solid #3498db;position:absolute}.resizer-handle[data-v-222aa227]{cursor:nwse-resize;pointer-events:auto;background:#3498db;border-radius:50%;width:15px;height:15px;position:absolute;bottom:-7px;right:-7px;box-shadow:0 0 5px #0003}.modal-overlay[data-v-222aa227]{z-index:2000;background:#000000b3;justify-content:center;align-items:center;width:100%;height:100%;display:flex;position:fixed;top:0;left:0}.modal-content[data-v-222aa227]{background:#fff;border-radius:12px;flex-direction:column;width:85%;max-height:85%;display:flex;overflow:hidden}.modal-header[data-v-222aa227]{background:#f8f9fa;border-bottom:1px solid #eee;justify-content:space-between;align-items:center;padding:15px 20px;display:flex}.preview-body[data-v-222aa227]{flex-grow:1;padding:30px;overflow-y:auto}.close-modal[data-v-222aa227]{color:#fff;cursor:pointer;background:#e74c3c;border:none;border-radius:4px;padding:5px 12px}.footer[data-v-222aa227]{color:#ecf0f1;background:#2c3e50;justify-content:space-between;padding:10px 20px;font-size:12px;display:flex}
2
+ /*$vite$:1*/
@@ -0,0 +1,245 @@
1
+ import { Teleport, createBlock, createCommentVNode, createElementBlock, createElementVNode, normalizeClass, normalizeStyle, onMounted, openBlock, ref, toDisplayString, withModifiers } from "vue";
2
+ var __plugin_vue_export_helper_default = (e, g) => {
3
+ let _ = e.__vccOpts || e;
4
+ for (let [e, v] of g) _[e] = v;
5
+ return _;
6
+ }, _hoisted_1 = { class: "toolbar" }, _hoisted_2 = { class: "group" }, _hoisted_3 = { class: "group" }, _hoisted_4 = { class: "group" }, _hoisted_5 = { class: "group" }, _hoisted_6 = { class: "group" }, _hoisted_7 = { class: "editor-container" }, _hoisted_8 = { class: "modal-content" }, _hoisted_9 = { class: "modal-header" }, _hoisted_10 = ["innerHTML"], _hoisted_11 = { class: "footer" }, editor_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7
+ __name: "editor",
8
+ setup(C) {
9
+ let w = ref(null), T = ref(null), E = ref(""), D = ref(0), O = ref(null), k = ref(!1), A = ref({
10
+ top: 0,
11
+ left: 0,
12
+ width: 0,
13
+ height: 0
14
+ }), j = (e, g = null) => {
15
+ document.execCommand(e, !1, g), U(), w.value.focus();
16
+ }, M = (e) => w.value ? document.queryCommandValue("formatBlock") === e.toLowerCase() : !1, N = () => {
17
+ let e = document.getElementById("editor-content-area");
18
+ if (window.html2pdf) window.html2pdf().set({
19
+ margin: 10,
20
+ filename: "belge.pdf",
21
+ image: {
22
+ type: "jpeg",
23
+ quality: .98
24
+ },
25
+ html2canvas: {
26
+ scale: 2,
27
+ useCORS: !0
28
+ },
29
+ jsPDF: {
30
+ unit: "mm",
31
+ format: "a4",
32
+ orientation: "portrait"
33
+ }
34
+ }).from(e).save();
35
+ else {
36
+ let e = document.createElement("script");
37
+ e.src = "https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js", e.onload = N, document.head.appendChild(e);
38
+ }
39
+ }, P = () => {
40
+ let e = "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'></head><body>" + document.getElementById("editor-content-area").innerHTML + "</body></html>", g = "data:application/vnd.ms-word;charset=utf-8," + encodeURIComponent(e), _ = document.createElement("a");
41
+ document.body.appendChild(_), _.href = g, _.download = "belge.doc", _.click(), document.body.removeChild(_);
42
+ }, F = () => {
43
+ let e = prompt("Satır sayısı:", "3"), g = prompt("Sütun sayısı:", "2");
44
+ if (e && g) {
45
+ let _ = "<table class=\"resizable-media\" style=\"width:100%; border-collapse:collapse; margin:10px 0; border:1px solid #ccc; display:inline-table;\">";
46
+ for (let v = 0; v < e; v++) {
47
+ _ += "<tr>";
48
+ for (let e = 0; e < g; e++) _ += "<td style=\"border:1px solid #ccc; padding:8px; min-width:50px;\">Hücre</td>";
49
+ _ += "</tr>";
50
+ }
51
+ _ += "</table><p>&nbsp;</p>", document.execCommand("insertHTML", !1, _), U();
52
+ }
53
+ }, I = () => {
54
+ let e = prompt("Youtube Video URL (örn: https://www.youtube.com/watch?v=dQw4w9WgXcQ):");
55
+ if (e) {
56
+ let g = `<iframe src="${`https://www.youtube.com/embed/${e.split("v=")[1]?.split("&")[0] || e.split("/").pop()}`}" class="resizable-media" style="width:400px; height:225px; display:inline-block;margin:5px;" frameborder="0" allowfullscreen></iframe>&nbsp;`;
57
+ document.execCommand("insertHTML", !1, g), U();
58
+ }
59
+ }, L = (e) => {
60
+ let g = e.target.files[0];
61
+ if (g) {
62
+ let _ = new FileReader();
63
+ _.onload = (e) => {
64
+ let g = `<img src="${e.target.result}" class="resizable-media" style="width:250px; display:inline-block; margin:5px;" />&nbsp;`;
65
+ document.execCommand("insertHTML", !1, g), U();
66
+ }, _.readAsDataURL(g), e.target.value = "";
67
+ }
68
+ }, R = () => {
69
+ let e = prompt("URL giriniz:", "https://");
70
+ e && j("createLink", e);
71
+ }, z = (e) => {
72
+ let g = e.target.closest(".resizable-media") || (e.target.tagName === "IMG" || e.target.tagName === "IFRAME" || e.target.tagName === "TABLE" ? e.target : null);
73
+ g && (g.classList.contains("resizable-media") || g.tagName === "IMG" || g.tagName === "IFRAME" || g.tagName === "TABLE") ? (O.value = g, B()) : O.value = null;
74
+ }, B = () => {
75
+ if (!O.value) return;
76
+ let e = O.value.getBoundingClientRect(), g = w.value.getBoundingClientRect();
77
+ A.value = {
78
+ top: e.top - g.top + w.value.scrollTop + "px",
79
+ left: e.left - g.left + "px",
80
+ width: e.width + "px",
81
+ height: e.height + "px"
82
+ };
83
+ }, V = (e) => {
84
+ let g = e.clientX, _ = O.value.clientWidth, v = O.value.clientHeight / _, y = (e) => {
85
+ let y = _ + (e.clientX - g);
86
+ O.value.style.width = y + "px", O.value.tagName === "IFRAME" ? O.value.style.height = y * v + "px" : O.value.style.height = "auto", B();
87
+ }, b = () => {
88
+ window.removeEventListener("mousemove", y), window.removeEventListener("mouseup", b), U();
89
+ };
90
+ window.addEventListener("mousemove", y), window.addEventListener("mouseup", b);
91
+ }, H = (e) => {
92
+ !e.target.closest(".resizable-media") && !e.target.classList.contains("resizer-handle") && (O.value = null);
93
+ }, U = () => {
94
+ w.value && (E.value = w.value.innerHTML, D.value = w.value.innerText.length);
95
+ };
96
+ return onMounted(() => {
97
+ w.value.innerHTML = "";
98
+ }), (x, S) => (openBlock(), createElementBlock("div", {
99
+ class: "custom-editor-wrapper",
100
+ onClick: H
101
+ }, [
102
+ createElementVNode("div", _hoisted_1, [
103
+ createElementVNode("div", _hoisted_2, [
104
+ createElementVNode("button", {
105
+ onClick: S[0] ||= (e) => j("formatBlock", "H1"),
106
+ class: normalizeClass({ active: M("H1") })
107
+ }, " H1 ", 2),
108
+ createElementVNode("button", {
109
+ onClick: S[1] ||= (e) => j("formatBlock", "H2"),
110
+ class: normalizeClass({ active: M("H2") })
111
+ }, " H2 ", 2),
112
+ createElementVNode("button", {
113
+ onClick: S[2] ||= (e) => j("formatBlock", "H3"),
114
+ class: normalizeClass({ active: M("H3") })
115
+ }, " H3 ", 2),
116
+ createElementVNode("button", {
117
+ onClick: S[3] ||= (e) => j("formatBlock", "P"),
118
+ class: normalizeClass({ active: M("P") })
119
+ }, " Metin ", 2)
120
+ ]),
121
+ S[22] ||= createElementVNode("div", { class: "divider" }, null, -1),
122
+ createElementVNode("div", _hoisted_3, [createElementVNode("select", {
123
+ onChange: S[4] ||= (e) => j("fontName", e.target.value),
124
+ class: "font-select"
125
+ }, [...S[17] ||= [
126
+ createElementVNode("option", { value: "Arial" }, "Arial", -1),
127
+ createElementVNode("option", { value: "Verdana" }, "Verdana", -1),
128
+ createElementVNode("option", { value: "Georgia" }, "Georgia", -1),
129
+ createElementVNode("option", { value: "Courier New" }, "Monospace", -1)
130
+ ]], 32), createElementVNode("select", {
131
+ onChange: S[5] ||= (e) => j("fontSize", e.target.value),
132
+ class: "size-select"
133
+ }, [...S[18] ||= [
134
+ createElementVNode("option", { value: "1" }, "S", -1),
135
+ createElementVNode("option", {
136
+ value: "3",
137
+ selected: ""
138
+ }, "M", -1),
139
+ createElementVNode("option", { value: "5" }, "L", -1),
140
+ createElementVNode("option", { value: "7" }, "XL", -1)
141
+ ]], 32)]),
142
+ S[23] ||= createElementVNode("div", { class: "divider" }, null, -1),
143
+ createElementVNode("div", _hoisted_4, [
144
+ createElementVNode("button", {
145
+ onClick: S[6] ||= (e) => j("bold"),
146
+ title: "Kalın"
147
+ }, [...S[19] ||= [createElementVNode("b", null, "B", -1)]]),
148
+ createElementVNode("button", {
149
+ onClick: S[7] ||= (e) => j("italic"),
150
+ title: "İtalik"
151
+ }, [...S[20] ||= [createElementVNode("i", null, "I", -1)]]),
152
+ createElementVNode("button", {
153
+ onClick: S[8] ||= (e) => j("underline"),
154
+ title: "Altı Çizili"
155
+ }, [...S[21] ||= [createElementVNode("u", null, "U", -1)]]),
156
+ createElementVNode("input", {
157
+ type: "color",
158
+ onInput: S[9] ||= (e) => j("foreColor", e.target.value),
159
+ class: "color-picker",
160
+ title: "Yazı Rengi"
161
+ }, null, 32)
162
+ ]),
163
+ S[24] ||= createElementVNode("div", { class: "divider" }, null, -1),
164
+ createElementVNode("div", _hoisted_5, [
165
+ createElementVNode("button", {
166
+ onClick: R,
167
+ title: "Link Ekle"
168
+ }, "🔗"),
169
+ createElementVNode("button", {
170
+ onClick: S[10] ||= (e) => x.$refs.fileInput.click(),
171
+ title: "Resim Ekle"
172
+ }, "🖼️"),
173
+ createElementVNode("button", {
174
+ onClick: I,
175
+ title: "Youtube Ekle"
176
+ }, "📺"),
177
+ createElementVNode("button", {
178
+ onClick: F,
179
+ title: "Tablo Ekle"
180
+ }, "📊"),
181
+ createElementVNode("input", {
182
+ type: "file",
183
+ ref_key: "fileInput",
184
+ ref: T,
185
+ hidden: "",
186
+ accept: "image/*",
187
+ onChange: L
188
+ }, null, 544)
189
+ ]),
190
+ S[25] ||= createElementVNode("div", { class: "divider" }, null, -1),
191
+ createElementVNode("div", _hoisted_6, [
192
+ createElementVNode("button", { onClick: S[11] ||= (e) => j("justifyLeft") }, "⬅️"),
193
+ createElementVNode("button", { onClick: S[12] ||= (e) => j("justifyCenter") }, "⬆️"),
194
+ createElementVNode("button", { onClick: S[13] ||= (e) => j("justifyRight") }, "➡️"),
195
+ createElementVNode("button", {
196
+ onClick: S[14] ||= (e) => k.value = !0,
197
+ class: "preview-btn",
198
+ title: "Ön İzleme"
199
+ }, " 👁️ ")
200
+ ]),
201
+ S[26] ||= createElementVNode("div", { class: "divider" }, null, -1),
202
+ createElementVNode("div", { class: "group" }, [createElementVNode("button", {
203
+ onClick: N,
204
+ class: "export-btn pdf",
205
+ title: "PDF İndir"
206
+ }, " 📄 PDF "), createElementVNode("button", {
207
+ onClick: P,
208
+ class: "export-btn word",
209
+ title: "Word İndir"
210
+ }, " 📝 DOC ")])
211
+ ]),
212
+ createElementVNode("div", _hoisted_7, [createElementVNode("div", {
213
+ id: "editor-content-area",
214
+ ref_key: "editorRef",
215
+ ref: w,
216
+ class: "editor-area",
217
+ contenteditable: "true",
218
+ onInput: U,
219
+ onClick: z
220
+ }, null, 544), O.value ? (openBlock(), createElementBlock("div", {
221
+ key: 0,
222
+ class: "resizer-overlay",
223
+ style: normalizeStyle(A.value)
224
+ }, [createElementVNode("div", {
225
+ class: "resizer-handle",
226
+ onMousedown: withModifiers(V, ["stop", "prevent"])
227
+ }, null, 32)], 4)) : createCommentVNode("", !0)]),
228
+ (openBlock(), createBlock(Teleport, { to: "body" }, [k.value ? (openBlock(), createElementBlock("div", {
229
+ key: 0,
230
+ class: "modal-overlay",
231
+ onClick: S[16] ||= withModifiers((e) => k.value = !1, ["self"])
232
+ }, [createElementVNode("div", _hoisted_8, [createElementVNode("div", _hoisted_9, [S[27] ||= createElementVNode("h3", null, "İçerik Ön İzleme", -1), createElementVNode("button", {
233
+ onClick: S[15] ||= (e) => k.value = !1,
234
+ class: "close-modal"
235
+ }, "X")]), createElementVNode("div", {
236
+ class: "preview-body",
237
+ innerHTML: E.value
238
+ }, null, 8, _hoisted_10)])])) : createCommentVNode("", !0)])),
239
+ createElementVNode("div", _hoisted_11, [createElementVNode("span", null, "Karakter: " + toDisplayString(D.value), 1), S[28] ||= createElementVNode("span", null, "Esnek Mod: Aktif (PDF/Word Aktarımı Eklendi)", -1)])
240
+ ]));
241
+ }
242
+ }, [["__scopeId", "data-v-222aa227"]]), rich_editor_1_default = { install: (e) => {
243
+ e.component("editor", editor_default);
244
+ } };
245
+ export { rich_editor_1_default as default, editor_default as editor };
@@ -0,0 +1 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`vue`)):typeof define==`function`&&define.amd?define([`exports`,`vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e[`vue-flex-editor`]={},e.Vue))})(this,function(e,t){Object.defineProperty(e,`__esModule`,{value:!0});var n=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},r={class:`toolbar`},i={class:`group`},a={class:`group`},o={class:`group`},s={class:`group`},c={class:`group`},l={class:`editor-container`},u={class:`modal-content`},d={class:`modal-header`},f=[`innerHTML`],p={class:`footer`},m=n({__name:`editor`,setup(e){let n=(0,t.ref)(null),m=(0,t.ref)(null),h=(0,t.ref)(``),g=(0,t.ref)(0),_=(0,t.ref)(null),v=(0,t.ref)(!1),y=(0,t.ref)({top:0,left:0,width:0,height:0}),b=(e,t=null)=>{document.execCommand(e,!1,t),M(),n.value.focus()},x=e=>n.value?document.queryCommandValue(`formatBlock`)===e.toLowerCase():!1,S=()=>{let e=document.getElementById(`editor-content-area`);if(window.html2pdf)window.html2pdf().set({margin:10,filename:`belge.pdf`,image:{type:`jpeg`,quality:.98},html2canvas:{scale:2,useCORS:!0},jsPDF:{unit:`mm`,format:`a4`,orientation:`portrait`}}).from(e).save();else{let e=document.createElement(`script`);e.src=`https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js`,e.onload=S,document.head.appendChild(e)}},C=()=>{let e=`<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'></head><body>`+document.getElementById(`editor-content-area`).innerHTML+`</body></html>`,t=`data:application/vnd.ms-word;charset=utf-8,`+encodeURIComponent(e),n=document.createElement(`a`);document.body.appendChild(n),n.href=t,n.download=`belge.doc`,n.click(),document.body.removeChild(n)},w=()=>{let e=prompt(`Satır sayısı:`,`3`),t=prompt(`Sütun sayısı:`,`2`);if(e&&t){let n=`<table class="resizable-media" style="width:100%; border-collapse:collapse; margin:10px 0; border:1px solid #ccc; display:inline-table;">`;for(let r=0;r<e;r++){n+=`<tr>`;for(let e=0;e<t;e++)n+=`<td style="border:1px solid #ccc; padding:8px; min-width:50px;">Hücre</td>`;n+=`</tr>`}n+=`</table><p>&nbsp;</p>`,document.execCommand(`insertHTML`,!1,n),M()}},T=()=>{let e=prompt(`Youtube Video URL (örn: https://www.youtube.com/watch?v=dQw4w9WgXcQ):`);if(e){let t=`<iframe src="${`https://www.youtube.com/embed/${e.split(`v=`)[1]?.split(`&`)[0]||e.split(`/`).pop()}`}" class="resizable-media" style="width:400px; height:225px; display:inline-block;margin:5px;" frameborder="0" allowfullscreen></iframe>&nbsp;`;document.execCommand(`insertHTML`,!1,t),M()}},E=e=>{let t=e.target.files[0];if(t){let n=new FileReader;n.onload=e=>{let t=`<img src="${e.target.result}" class="resizable-media" style="width:250px; display:inline-block; margin:5px;" />&nbsp;`;document.execCommand(`insertHTML`,!1,t),M()},n.readAsDataURL(t),e.target.value=``}},D=()=>{let e=prompt(`URL giriniz:`,`https://`);e&&b(`createLink`,e)},O=e=>{let t=e.target.closest(`.resizable-media`)||(e.target.tagName===`IMG`||e.target.tagName===`IFRAME`||e.target.tagName===`TABLE`?e.target:null);t&&(t.classList.contains(`resizable-media`)||t.tagName===`IMG`||t.tagName===`IFRAME`||t.tagName===`TABLE`)?(_.value=t,k()):_.value=null},k=()=>{if(!_.value)return;let e=_.value.getBoundingClientRect(),t=n.value.getBoundingClientRect();y.value={top:e.top-t.top+n.value.scrollTop+`px`,left:e.left-t.left+`px`,width:e.width+`px`,height:e.height+`px`}},A=e=>{let t=e.clientX,n=_.value.clientWidth,r=_.value.clientHeight/n,i=e=>{let i=n+(e.clientX-t);_.value.style.width=i+`px`,_.value.tagName===`IFRAME`?_.value.style.height=i*r+`px`:_.value.style.height=`auto`,k()},a=()=>{window.removeEventListener(`mousemove`,i),window.removeEventListener(`mouseup`,a),M()};window.addEventListener(`mousemove`,i),window.addEventListener(`mouseup`,a)},j=e=>{!e.target.closest(`.resizable-media`)&&!e.target.classList.contains(`resizer-handle`)&&(_.value=null)},M=()=>{n.value&&(h.value=n.value.innerHTML,g.value=n.value.innerText.length)};return(0,t.onMounted)(()=>{n.value.innerHTML=``}),(e,k)=>((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{class:`custom-editor-wrapper`,onClick:j},[(0,t.createElementVNode)(`div`,r,[(0,t.createElementVNode)(`div`,i,[(0,t.createElementVNode)(`button`,{onClick:k[0]||=e=>b(`formatBlock`,`H1`),class:(0,t.normalizeClass)({active:x(`H1`)})},` H1 `,2),(0,t.createElementVNode)(`button`,{onClick:k[1]||=e=>b(`formatBlock`,`H2`),class:(0,t.normalizeClass)({active:x(`H2`)})},` H2 `,2),(0,t.createElementVNode)(`button`,{onClick:k[2]||=e=>b(`formatBlock`,`H3`),class:(0,t.normalizeClass)({active:x(`H3`)})},` H3 `,2),(0,t.createElementVNode)(`button`,{onClick:k[3]||=e=>b(`formatBlock`,`P`),class:(0,t.normalizeClass)({active:x(`P`)})},` Metin `,2)]),k[22]||=(0,t.createElementVNode)(`div`,{class:`divider`},null,-1),(0,t.createElementVNode)(`div`,a,[(0,t.createElementVNode)(`select`,{onChange:k[4]||=e=>b(`fontName`,e.target.value),class:`font-select`},[...k[17]||=[(0,t.createElementVNode)(`option`,{value:`Arial`},`Arial`,-1),(0,t.createElementVNode)(`option`,{value:`Verdana`},`Verdana`,-1),(0,t.createElementVNode)(`option`,{value:`Georgia`},`Georgia`,-1),(0,t.createElementVNode)(`option`,{value:`Courier New`},`Monospace`,-1)]],32),(0,t.createElementVNode)(`select`,{onChange:k[5]||=e=>b(`fontSize`,e.target.value),class:`size-select`},[...k[18]||=[(0,t.createElementVNode)(`option`,{value:`1`},`S`,-1),(0,t.createElementVNode)(`option`,{value:`3`,selected:``},`M`,-1),(0,t.createElementVNode)(`option`,{value:`5`},`L`,-1),(0,t.createElementVNode)(`option`,{value:`7`},`XL`,-1)]],32)]),k[23]||=(0,t.createElementVNode)(`div`,{class:`divider`},null,-1),(0,t.createElementVNode)(`div`,o,[(0,t.createElementVNode)(`button`,{onClick:k[6]||=e=>b(`bold`),title:`Kalın`},[...k[19]||=[(0,t.createElementVNode)(`b`,null,`B`,-1)]]),(0,t.createElementVNode)(`button`,{onClick:k[7]||=e=>b(`italic`),title:`İtalik`},[...k[20]||=[(0,t.createElementVNode)(`i`,null,`I`,-1)]]),(0,t.createElementVNode)(`button`,{onClick:k[8]||=e=>b(`underline`),title:`Altı Çizili`},[...k[21]||=[(0,t.createElementVNode)(`u`,null,`U`,-1)]]),(0,t.createElementVNode)(`input`,{type:`color`,onInput:k[9]||=e=>b(`foreColor`,e.target.value),class:`color-picker`,title:`Yazı Rengi`},null,32)]),k[24]||=(0,t.createElementVNode)(`div`,{class:`divider`},null,-1),(0,t.createElementVNode)(`div`,s,[(0,t.createElementVNode)(`button`,{onClick:D,title:`Link Ekle`},`🔗`),(0,t.createElementVNode)(`button`,{onClick:k[10]||=t=>e.$refs.fileInput.click(),title:`Resim Ekle`},`🖼️`),(0,t.createElementVNode)(`button`,{onClick:T,title:`Youtube Ekle`},`📺`),(0,t.createElementVNode)(`button`,{onClick:w,title:`Tablo Ekle`},`📊`),(0,t.createElementVNode)(`input`,{type:`file`,ref_key:`fileInput`,ref:m,hidden:``,accept:`image/*`,onChange:E},null,544)]),k[25]||=(0,t.createElementVNode)(`div`,{class:`divider`},null,-1),(0,t.createElementVNode)(`div`,c,[(0,t.createElementVNode)(`button`,{onClick:k[11]||=e=>b(`justifyLeft`)},`⬅️`),(0,t.createElementVNode)(`button`,{onClick:k[12]||=e=>b(`justifyCenter`)},`⬆️`),(0,t.createElementVNode)(`button`,{onClick:k[13]||=e=>b(`justifyRight`)},`➡️`),(0,t.createElementVNode)(`button`,{onClick:k[14]||=e=>v.value=!0,class:`preview-btn`,title:`Ön İzleme`},` 👁️ `)]),k[26]||=(0,t.createElementVNode)(`div`,{class:`divider`},null,-1),(0,t.createElementVNode)(`div`,{class:`group`},[(0,t.createElementVNode)(`button`,{onClick:S,class:`export-btn pdf`,title:`PDF İndir`},` 📄 PDF `),(0,t.createElementVNode)(`button`,{onClick:C,class:`export-btn word`,title:`Word İndir`},` 📝 DOC `)])]),(0,t.createElementVNode)(`div`,l,[(0,t.createElementVNode)(`div`,{id:`editor-content-area`,ref_key:`editorRef`,ref:n,class:`editor-area`,contenteditable:`true`,onInput:M,onClick:O},null,544),_.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,class:`resizer-overlay`,style:(0,t.normalizeStyle)(y.value)},[(0,t.createElementVNode)(`div`,{class:`resizer-handle`,onMousedown:(0,t.withModifiers)(A,[`stop`,`prevent`])},null,32)],4)):(0,t.createCommentVNode)(``,!0)]),((0,t.openBlock)(),(0,t.createBlock)(t.Teleport,{to:`body`},[v.value?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{key:0,class:`modal-overlay`,onClick:k[16]||=(0,t.withModifiers)(e=>v.value=!1,[`self`])},[(0,t.createElementVNode)(`div`,u,[(0,t.createElementVNode)(`div`,d,[k[27]||=(0,t.createElementVNode)(`h3`,null,`İçerik Ön İzleme`,-1),(0,t.createElementVNode)(`button`,{onClick:k[15]||=e=>v.value=!1,class:`close-modal`},`X`)]),(0,t.createElementVNode)(`div`,{class:`preview-body`,innerHTML:h.value},null,8,f)])])):(0,t.createCommentVNode)(``,!0)])),(0,t.createElementVNode)(`div`,p,[(0,t.createElementVNode)(`span`,null,`Karakter: `+(0,t.toDisplayString)(g.value),1),k[28]||=(0,t.createElementVNode)(`span`,null,`Esnek Mod: Aktif (PDF/Word Aktarımı Eklendi)`,-1)])]))}},[[`__scopeId`,`data-v-222aa227`]]);e.default={install:e=>{e.component(`editor`,m)}},e.editor=m});
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>my-vue-library</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.js"></script>
12
+ </body>
13
+ </html>
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import editor from "./src/components/editor.vue";
2
+ export default {
3
+ install: (app) => {
4
+ app.component("editor", editor);
5
+ },
6
+ };
7
+
8
+ export { editor };
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "vue-flexible-text-editor",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "https://github.com/solen13/rich-editor"
6
+ },
7
+ "private": false,
8
+ "version": "1.0.1",
9
+ "type": "module",
10
+ "scripts": {
11
+ "dev": "vite",
12
+ "build": "vite build",
13
+ "preview": "vite preview"
14
+ },
15
+ "dependencies": {
16
+ "vue": "^3.5.24"
17
+ },
18
+ "devDependencies": {
19
+ "@vitejs/plugin-vue": "^6.0.1",
20
+ "vite": "npm:rolldown-vite@7.2.5"
21
+ },
22
+ "overrides": {
23
+ "vite": "npm:rolldown-vite@7.2.5"
24
+ }
25
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/src/App.vue ADDED
@@ -0,0 +1,9 @@
1
+ <script setup>
2
+ import Editor from "./components/editor.vue";
3
+ </script>
4
+
5
+ <template>
6
+ <Editor></Editor>
7
+ </template>
8
+
9
+ <style scoped></style>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
@@ -0,0 +1,518 @@
1
+ <template>
2
+ <div class="custom-editor-wrapper" @click="deselectMedia">
3
+ <div class="toolbar">
4
+ <div class="group">
5
+ <button
6
+ @click="execAction('formatBlock', 'H1')"
7
+ :class="{ active: isTag('H1') }"
8
+ >
9
+ H1
10
+ </button>
11
+ <button
12
+ @click="execAction('formatBlock', 'H2')"
13
+ :class="{ active: isTag('H2') }"
14
+ >
15
+ H2
16
+ </button>
17
+ <button
18
+ @click="execAction('formatBlock', 'H3')"
19
+ :class="{ active: isTag('H3') }"
20
+ >
21
+ H3
22
+ </button>
23
+ <button
24
+ @click="execAction('formatBlock', 'P')"
25
+ :class="{ active: isTag('P') }"
26
+ >
27
+ Metin
28
+ </button>
29
+ </div>
30
+
31
+ <div class="divider"></div>
32
+
33
+ <div class="group">
34
+ <select
35
+ @change="execAction('fontName', $event.target.value)"
36
+ class="font-select"
37
+ >
38
+ <option value="Arial">Arial</option>
39
+ <option value="Verdana">Verdana</option>
40
+ <option value="Georgia">Georgia</option>
41
+ <option value="Courier New">Monospace</option>
42
+ </select>
43
+ <select
44
+ @change="execAction('fontSize', $event.target.value)"
45
+ class="size-select"
46
+ >
47
+ <option value="1">S</option>
48
+ <option value="3" selected>M</option>
49
+ <option value="5">L</option>
50
+ <option value="7">XL</option>
51
+ </select>
52
+ </div>
53
+
54
+ <div class="divider"></div>
55
+
56
+ <div class="group">
57
+ <button @click="execAction('bold')" title="Kalın"><b>B</b></button>
58
+ <button @click="execAction('italic')" title="İtalik"><i>I</i></button>
59
+ <button @click="execAction('underline')" title="Altı Çizili">
60
+ <u>U</u>
61
+ </button>
62
+ <input
63
+ type="color"
64
+ @input="execAction('foreColor', $event.target.value)"
65
+ class="color-picker"
66
+ title="Yazı Rengi"
67
+ />
68
+ </div>
69
+
70
+ <div class="divider"></div>
71
+
72
+ <div class="group">
73
+ <button @click="addLink" title="Link Ekle">🔗</button>
74
+ <button @click="$refs.fileInput.click()" title="Resim Ekle">🖼️</button>
75
+ <button @click="addYoutubeVideo" title="Youtube Ekle">📺</button>
76
+ <button @click="insertTable" title="Tablo Ekle">📊</button>
77
+ <input
78
+ type="file"
79
+ ref="fileInput"
80
+ hidden
81
+ accept="image/*"
82
+ @change="handleImageUpload"
83
+ />
84
+ </div>
85
+
86
+ <div class="divider"></div>
87
+
88
+ <div class="group">
89
+ <button @click="execAction('justifyLeft')">⬅️</button>
90
+ <button @click="execAction('justifyCenter')">⬆️</button>
91
+ <button @click="execAction('justifyRight')">➡️</button>
92
+ <button
93
+ @click="showPreview = true"
94
+ class="preview-btn"
95
+ title="Ön İzleme"
96
+ >
97
+ 👁️
98
+ </button>
99
+ </div>
100
+
101
+ <div class="divider"></div>
102
+
103
+ <div class="group">
104
+ <button @click="exportToPDF" class="export-btn pdf" title="PDF İndir">
105
+ 📄 PDF
106
+ </button>
107
+ <button
108
+ @click="exportToWord"
109
+ class="export-btn word"
110
+ title="Word İndir"
111
+ >
112
+ 📝 DOC
113
+ </button>
114
+ </div>
115
+ </div>
116
+
117
+ <div class="editor-container">
118
+ <div
119
+ id="editor-content-area"
120
+ ref="editorRef"
121
+ class="editor-area"
122
+ contenteditable="true"
123
+ @input="updateContent"
124
+ @click="handleEditorClick"
125
+ ></div>
126
+
127
+ <div v-if="selectedMedia" class="resizer-overlay" :style="resizerStyle">
128
+ <div
129
+ class="resizer-handle"
130
+ @mousedown.stop.prevent="startResizing"
131
+ ></div>
132
+ </div>
133
+ </div>
134
+
135
+ <Teleport to="body">
136
+ <div
137
+ v-if="showPreview"
138
+ class="modal-overlay"
139
+ @click.self="showPreview = false"
140
+ >
141
+ <div class="modal-content">
142
+ <div class="modal-header">
143
+ <h3>İçerik Ön İzleme</h3>
144
+ <button @click="showPreview = false" class="close-modal">X</button>
145
+ </div>
146
+ <div class="preview-body" v-html="content"></div>
147
+ </div>
148
+ </div>
149
+ </Teleport>
150
+
151
+ <div class="footer">
152
+ <span>Karakter: {{ charCount }}</span>
153
+ <span>Esnek Mod: Aktif (PDF/Word Aktarımı Eklendi)</span>
154
+ </div>
155
+ </div>
156
+ </template>
157
+
158
+ <script setup>
159
+ import { ref, onMounted } from "vue";
160
+
161
+ const editorRef = ref(null);
162
+ const fileInput = ref(null);
163
+ const content = ref("");
164
+ const charCount = ref(0);
165
+ const selectedMedia = ref(null);
166
+ const showPreview = ref(false);
167
+ const resizerStyle = ref({ top: 0, left: 0, width: 0, height: 0 });
168
+
169
+ const execAction = (command, value = null) => {
170
+ document.execCommand(command, false, value);
171
+ updateContent();
172
+ editorRef.value.focus();
173
+ };
174
+
175
+ const isTag = (tagName) => {
176
+ if (!editorRef.value) return false;
177
+ return document.queryCommandValue("formatBlock") === tagName.toLowerCase();
178
+ };
179
+
180
+ const exportToPDF = () => {
181
+ const element = document.getElementById("editor-content-area");
182
+
183
+ if (window.html2pdf) {
184
+ const opt = {
185
+ margin: 10,
186
+ filename: "belge.pdf",
187
+ image: { type: "jpeg", quality: 0.98 },
188
+ html2canvas: { scale: 2, useCORS: true },
189
+ jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
190
+ };
191
+ window.html2pdf().set(opt).from(element).save();
192
+ } else {
193
+ const script = document.createElement("script");
194
+ script.src =
195
+ "https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js";
196
+ script.onload = exportToPDF;
197
+ document.head.appendChild(script);
198
+ }
199
+ };
200
+
201
+ const exportToWord = () => {
202
+ const header =
203
+ "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'></head><body>";
204
+ const footer = "</body></html>";
205
+ const sourceHTML =
206
+ header + document.getElementById("editor-content-area").innerHTML + footer;
207
+
208
+ const source =
209
+ "data:application/vnd.ms-word;charset=utf-8," +
210
+ encodeURIComponent(sourceHTML);
211
+ const fileDownload = document.createElement("a");
212
+ document.body.appendChild(fileDownload);
213
+ fileDownload.href = source;
214
+ fileDownload.download = "belge.doc";
215
+ fileDownload.click();
216
+ document.body.removeChild(fileDownload);
217
+ };
218
+
219
+ const insertTable = () => {
220
+ const rows = prompt("Satır sayısı:", "3");
221
+ const cols = prompt("Sütun sayısı:", "2");
222
+ if (rows && cols) {
223
+ let tableHtml = `<table class="resizable-media" style="width:100%; border-collapse:collapse; margin:10px 0; border:1px solid #ccc; display:inline-table;">`;
224
+ for (let i = 0; i < rows; i++) {
225
+ tableHtml += "<tr>";
226
+ for (let j = 0; j < cols; j++) {
227
+ tableHtml += `<td style="border:1px solid #ccc; padding:8px; min-width:50px;">Hücre</td>`;
228
+ }
229
+ tableHtml += "</tr>";
230
+ }
231
+ tableHtml += "</table><p>&nbsp;</p>";
232
+ document.execCommand("insertHTML", false, tableHtml);
233
+ updateContent();
234
+ }
235
+ };
236
+
237
+ const addYoutubeVideo = () => {
238
+ const url = prompt(
239
+ "Youtube Video URL (örn: https://www.youtube.com/watch?v=dQw4w9WgXcQ):"
240
+ );
241
+ if (url) {
242
+ const videoId = url.split("v=")[1]?.split("&")[0] || url.split("/").pop();
243
+ const embedUrl = `https://www.youtube.com/embed/${videoId}`;
244
+ const html = `<iframe src="${embedUrl}" class="resizable-media" style="width:400px; height:225px; display:inline-block;margin:5px;" frameborder="0" allowfullscreen></iframe>&nbsp;`;
245
+ document.execCommand("insertHTML", false, html);
246
+ updateContent();
247
+ }
248
+ };
249
+
250
+ const handleImageUpload = (e) => {
251
+ const file = e.target.files[0];
252
+ if (file) {
253
+ const reader = new FileReader();
254
+ reader.onload = (ev) => {
255
+ const html = `<img src="${ev.target.result}" class="resizable-media" style="width:250px; display:inline-block; margin:5px;" />&nbsp;`;
256
+ document.execCommand("insertHTML", false, html);
257
+ updateContent();
258
+ };
259
+ reader.readAsDataURL(file);
260
+ e.target.value = "";
261
+ }
262
+ };
263
+
264
+ const addLink = () => {
265
+ const url = prompt("URL giriniz:", "https://");
266
+ if (url) execAction("createLink", url);
267
+ };
268
+
269
+ const handleEditorClick = (e) => {
270
+ const target =
271
+ e.target.closest(".resizable-media") ||
272
+ (e.target.tagName === "IMG" ||
273
+ e.target.tagName === "IFRAME" ||
274
+ e.target.tagName === "TABLE"
275
+ ? e.target
276
+ : null);
277
+ if (
278
+ target &&
279
+ (target.classList.contains("resizable-media") ||
280
+ target.tagName === "IMG" ||
281
+ target.tagName === "IFRAME" ||
282
+ target.tagName === "TABLE")
283
+ ) {
284
+ selectedMedia.value = target;
285
+ updateResizerPosition();
286
+ } else {
287
+ selectedMedia.value = null;
288
+ }
289
+ };
290
+
291
+ const updateResizerPosition = () => {
292
+ if (!selectedMedia.value) return;
293
+ const rect = selectedMedia.value.getBoundingClientRect();
294
+ const containerRect = editorRef.value.getBoundingClientRect();
295
+ resizerStyle.value = {
296
+ top: rect.top - containerRect.top + editorRef.value.scrollTop + "px",
297
+ left: rect.left - containerRect.left + "px",
298
+ width: rect.width + "px",
299
+ height: rect.height + "px",
300
+ };
301
+ };
302
+
303
+ const startResizing = (e) => {
304
+ const startX = e.clientX;
305
+ const startWidth = selectedMedia.value.clientWidth;
306
+ const ratio = selectedMedia.value.clientHeight / startWidth;
307
+ const onMouseMove = (mE) => {
308
+ const newWidth = startWidth + (mE.clientX - startX);
309
+ selectedMedia.value.style.width = newWidth + "px";
310
+ if (selectedMedia.value.tagName === "IFRAME") {
311
+ selectedMedia.value.style.height = newWidth * ratio + "px";
312
+ } else {
313
+ selectedMedia.value.style.height = "auto";
314
+ }
315
+ updateResizerPosition();
316
+ };
317
+ const onMouseUp = () => {
318
+ window.removeEventListener("mousemove", onMouseMove);
319
+ window.removeEventListener("mouseup", onMouseUp);
320
+ updateContent();
321
+ };
322
+ window.addEventListener("mousemove", onMouseMove);
323
+ window.addEventListener("mouseup", onMouseUp);
324
+ };
325
+
326
+ const deselectMedia = (e) => {
327
+ if (
328
+ !e.target.closest(".resizable-media") &&
329
+ !e.target.classList.contains("resizer-handle")
330
+ ) {
331
+ selectedMedia.value = null;
332
+ }
333
+ };
334
+
335
+ const updateContent = () => {
336
+ if (editorRef.value) {
337
+ content.value = editorRef.value.innerHTML;
338
+ charCount.value = editorRef.value.innerText.length;
339
+ }
340
+ };
341
+
342
+ onMounted(() => {
343
+ editorRef.value.innerHTML = "";
344
+ });
345
+ </script>
346
+
347
+ <style scoped>
348
+ /* Mevcut stiller korunuyor */
349
+ .custom-editor-wrapper {
350
+ border: 2px solid #2c3e50;
351
+ border-radius: 12px;
352
+ overflow: hidden;
353
+ background: #fff;
354
+ font-family: "Segoe UI", sans-serif;
355
+ }
356
+ .toolbar {
357
+ display: flex;
358
+ flex-wrap: wrap;
359
+ gap: 10px;
360
+ padding: 12px;
361
+ background: #f8f9fa;
362
+ border-bottom: 2px solid #eee;
363
+ align-items: center;
364
+ }
365
+ .group {
366
+ display: flex;
367
+ gap: 5px;
368
+ align-items: center;
369
+ }
370
+ .toolbar button {
371
+ padding: 6px 10px;
372
+ border: 1px solid #ddd;
373
+ background: white;
374
+ cursor: pointer;
375
+ border-radius: 4px;
376
+ font-weight: 600;
377
+ transition: 0.2s;
378
+ }
379
+ .toolbar button:hover {
380
+ background: #e9ecef;
381
+ }
382
+ .preview-btn {
383
+ background: #27ae60 !important;
384
+ color: white !important;
385
+ border: none !important;
386
+ }
387
+
388
+ /* YENİ: Dışa Aktar Buton Stilleri */
389
+ .export-btn.pdf {
390
+ background: #e74c3c !important;
391
+ color: white !important;
392
+ border: none !important;
393
+ }
394
+ .export-btn.word {
395
+ background: #2980b9 !important;
396
+ color: white !important;
397
+ border: none !important;
398
+ }
399
+
400
+ .toolbar button.active {
401
+ background: #3498db;
402
+ color: white;
403
+ border-color: #2980b9;
404
+ }
405
+ .font-select,
406
+ .size-select {
407
+ padding: 5px;
408
+ border-radius: 4px;
409
+ border: 1px solid #ddd;
410
+ }
411
+ .color-picker {
412
+ width: 30px;
413
+ height: 30px;
414
+ padding: 0;
415
+ border: none;
416
+ cursor: pointer;
417
+ }
418
+ .divider {
419
+ width: 1px;
420
+ height: 30px;
421
+ background: #ddd;
422
+ }
423
+ .editor-container {
424
+ position: relative;
425
+ }
426
+ .editor-area {
427
+ min-height: 400px;
428
+ padding: 30px;
429
+ outline: none;
430
+ line-height: 1.6;
431
+ font-size: 16px;
432
+ }
433
+
434
+ /* Resizer ve Medya Stilleri (Mevcut olanlarla aynı) */
435
+ :deep(.resizable-media) {
436
+ cursor: pointer;
437
+ margin: 10px 5px;
438
+ display: inline-block;
439
+ vertical-align: top;
440
+ border: 2px solid transparent;
441
+ }
442
+ :deep(table.resizable-media) {
443
+ border-collapse: collapse;
444
+ }
445
+ :deep(.resizable-media:hover) {
446
+ border-color: #3498db;
447
+ }
448
+ .resizer-overlay {
449
+ position: absolute;
450
+ pointer-events: none;
451
+ border: 2px solid #3498db;
452
+ z-index: 10;
453
+ }
454
+ .resizer-handle {
455
+ position: absolute;
456
+ width: 15px;
457
+ height: 15px;
458
+ background: #3498db;
459
+ right: -7px;
460
+ bottom: -7px;
461
+ cursor: nwse-resize;
462
+ pointer-events: auto;
463
+ border-radius: 50%;
464
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
465
+ }
466
+
467
+ /* Modal Stilleri (Mevcut olanlarla aynı) */
468
+ .modal-overlay {
469
+ position: fixed;
470
+ top: 0;
471
+ left: 0;
472
+ width: 100%;
473
+ height: 100%;
474
+ background: rgba(0, 0, 0, 0.7);
475
+ display: flex;
476
+ justify-content: center;
477
+ align-items: center;
478
+ z-index: 2000;
479
+ }
480
+ .modal-content {
481
+ background: #fff;
482
+ width: 85%;
483
+ max-height: 85%;
484
+ border-radius: 12px;
485
+ display: flex;
486
+ flex-direction: column;
487
+ overflow: hidden;
488
+ }
489
+ .modal-header {
490
+ padding: 15px 20px;
491
+ background: #f8f9fa;
492
+ border-bottom: 1px solid #eee;
493
+ display: flex;
494
+ justify-content: space-between;
495
+ align-items: center;
496
+ }
497
+ .preview-body {
498
+ padding: 30px;
499
+ overflow-y: auto;
500
+ flex-grow: 1;
501
+ }
502
+ .close-modal {
503
+ background: #e74c3c;
504
+ color: white;
505
+ border: none;
506
+ padding: 5px 12px;
507
+ border-radius: 4px;
508
+ cursor: pointer;
509
+ }
510
+ .footer {
511
+ display: flex;
512
+ justify-content: space-between;
513
+ padding: 10px 20px;
514
+ background: #2c3e50;
515
+ color: #ecf0f1;
516
+ font-size: 12px;
517
+ }
518
+ </style>
package/src/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import editor from "./components/editor.vue";
2
+
3
+ export { editor };
4
+
5
+ export default {
6
+ install: (app) => {
7
+ app.component("editor", editor);
8
+ },
9
+ };
package/src/main.js ADDED
@@ -0,0 +1,9 @@
1
+ import { createApp } from "vue";
2
+ import App from "./App.vue";
3
+ import editor from "./components/editor.vue";
4
+
5
+ const app = createApp(App);
6
+
7
+ app.use(editor);
8
+
9
+ app.mount("#app");
package/src/style.css ADDED
File without changes
package/vite.config.js ADDED
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from "vite";
2
+ import vue from "@vitejs/plugin-vue";
3
+ import { resolve } from "path";
4
+
5
+ export default defineConfig({
6
+ plugins: [vue()],
7
+ build: {
8
+ lib: {
9
+ entry: resolve(__dirname, "index.js"),
10
+ name: "vue-flex-editor",
11
+ fileName: "vue-flex-editor",
12
+ },
13
+ rollupOptions: {
14
+ external: ["vue"], // Vue'yu paketin içine gömme, dışarıdan kullansın
15
+ output: {
16
+ globals: { vue: "Vue" },
17
+ },
18
+ },
19
+ },
20
+ });