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 +5 -0
- package/dist/vite.svg +1 -0
- package/dist/vue-flex-editor.css +2 -0
- package/dist/vue-flex-editor.js +245 -0
- package/dist/vue-flex-editor.umd.cjs +1 -0
- package/index.html +13 -0
- package/index.js +8 -0
- package/package.json +25 -0
- package/public/vite.svg +1 -0
- package/src/App.vue +9 -0
- package/src/assets/vue.svg +1 -0
- package/src/components/editor.vue +518 -0
- package/src/index.js +9 -0
- package/src/main.js +9 -0
- package/src/style.css +0 -0
- package/vite.config.js +20 -0
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> </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> `;
|
|
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;" /> `;
|
|
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> </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> `;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;" /> `;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
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
|
+
}
|
package/public/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>
|
package/src/App.vue
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="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> </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> `;
|
|
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;" /> `;
|
|
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
package/src/main.js
ADDED
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
|
+
});
|