UNPKG

vue-libre-editor

Version:
1,254 lines (1,119 loc) 152 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) : typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.VueLibreEditor = {}, global.Vue)); })(this, (function (exports, vue) { 'use strict'; /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); const toCamelCase = (string) => string.replace( /^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase() ); const toPascalCase = (string) => { const camelCase = toCamelCase(string); return camelCase.charAt(0).toUpperCase() + camelCase.slice(1); }; const mergeClasses = (...classes) => classes.filter((className, index, array) => { return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index; }).join(" ").trim(); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ var defaultAttributes = { xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": 2, "stroke-linecap": "round", "stroke-linejoin": "round" }; /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Icon = ({ size, strokeWidth = 2, absoluteStrokeWidth, color, iconNode, name, class: classes, ...props }, { slots }) => { return vue.h( "svg", { ...defaultAttributes, width: size || defaultAttributes.width, height: size || defaultAttributes.height, stroke: color || defaultAttributes.stroke, "stroke-width": absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth, class: mergeClasses( "lucide", ...name ? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`] : ["lucide-icon"] ), ...props }, [...iconNode.map((child) => vue.h(...child)), ...slots.default ? [slots.default()] : []] ); }; /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const createLucideIcon = (iconName, iconNode) => (props, { slots }) => vue.h( Icon, { ...props, iconNode, name: iconName }, slots ); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const AlignCenter = createLucideIcon("align-center", [ ["path", { d: "M17 12H7", key: "16if0g" }], ["path", { d: "M19 18H5", key: "18s9l3" }], ["path", { d: "M21 6H3", key: "1jwq7v" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const AlignJustify = createLucideIcon("align-justify", [ ["path", { d: "M3 12h18", key: "1i2n21" }], ["path", { d: "M3 18h18", key: "1h113x" }], ["path", { d: "M3 6h18", key: "d0wm0j" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const AlignLeft = createLucideIcon("align-left", [ ["path", { d: "M15 12H3", key: "6jk70r" }], ["path", { d: "M17 18H3", key: "1amg6g" }], ["path", { d: "M21 6H3", key: "1jwq7v" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const AlignRight = createLucideIcon("align-right", [ ["path", { d: "M21 12H9", key: "dn1m92" }], ["path", { d: "M21 18H7", key: "1ygte8" }], ["path", { d: "M21 6H3", key: "1jwq7v" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Bold = createLucideIcon("bold", [ [ "path", { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" } ] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Combine = createLucideIcon("combine", [ ["path", { d: "M10 18H5a3 3 0 0 1-3-3v-1", key: "ru65g8" }], ["path", { d: "M14 2a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2", key: "e30een" }], ["path", { d: "M20 2a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2", key: "2ahx8o" }], ["path", { d: "m7 21 3-3-3-3", key: "127cv2" }], ["rect", { x: "14", y: "14", width: "8", height: "8", rx: "2", key: "1b0bso" }], ["rect", { x: "2", y: "2", width: "8", height: "8", rx: "2", key: "1x09vl" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Image$1 = createLucideIcon("image", [ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }], ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }], ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const IndentDecrease = createLucideIcon("indent-decrease", [ ["path", { d: "M21 12H11", key: "wd7e0v" }], ["path", { d: "M21 18H11", key: "4wu86t" }], ["path", { d: "M21 6H11", key: "6dy1d6" }], ["path", { d: "m7 8-4 4 4 4", key: "o5hrat" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const IndentIncrease = createLucideIcon("indent-increase", [ ["path", { d: "M21 12H11", key: "wd7e0v" }], ["path", { d: "M21 18H11", key: "4wu86t" }], ["path", { d: "M21 6H11", key: "6dy1d6" }], ["path", { d: "m3 8 4 4-4 4", key: "1a3j6y" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Italic = createLucideIcon("italic", [ ["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }], ["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }], ["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Link = createLucideIcon("link", [ ["path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71", key: "1cjeqo" }], ["path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71", key: "19qd67" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Paintbrush = createLucideIcon("paintbrush", [ ["path", { d: "m14.622 17.897-10.68-2.913", key: "vj2p1u" }], [ "path", { d: "M18.376 2.622a1 1 0 1 1 3.002 3.002L17.36 9.643a.5.5 0 0 0 0 .707l.944.944a2.41 2.41 0 0 1 0 3.408l-.944.944a.5.5 0 0 1-.707 0L8.354 7.348a.5.5 0 0 1 0-.707l.944-.944a2.41 2.41 0 0 1 3.408 0l.944.944a.5.5 0 0 0 .707 0z", key: "18tc5c" } ], [ "path", { d: "M9 8c-1.804 2.71-3.97 3.46-6.583 3.948a.507.507 0 0 0-.302.819l7.32 8.883a1 1 0 0 0 1.185.204C12.735 20.405 16 16.792 16 15", key: "ytzfxy" } ] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Redo = createLucideIcon("redo", [ ["path", { d: "M21 7v6h-6", key: "3ptur4" }], ["path", { d: "M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7", key: "1kgawr" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Settings = createLucideIcon("settings", [ [ "path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z", key: "1qme2f" } ], ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const SquareSplitVertical = createLucideIcon("square-split-vertical", [ ["path", { d: "M5 8V5c0-1 1-2 2-2h10c1 0 2 1 2 2v3", key: "1pi83i" }], ["path", { d: "M19 16v3c0 1-1 2-2 2H7c-1 0-2-1-2-2v-3", key: "ido5k7" }], ["line", { x1: "4", x2: "20", y1: "12", y2: "12", key: "1e0a9i" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Strikethrough = createLucideIcon("strikethrough", [ ["path", { d: "M16 4H9a3 3 0 0 0-2.83 4", key: "43sutm" }], ["path", { d: "M14 12a4 4 0 0 1 0 8H6", key: "nlfj13" }], ["line", { x1: "4", x2: "20", y1: "12", y2: "12", key: "1e0a9i" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Table = createLucideIcon("table", [ ["path", { d: "M12 3v18", key: "108xh3" }], ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }], ["path", { d: "M3 9h18", key: "1pudct" }], ["path", { d: "M3 15h18", key: "5xshup" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Underline = createLucideIcon("underline", [ ["path", { d: "M6 4v6a6 6 0 0 0 12 0V4", key: "9kb039" }], ["line", { x1: "4", x2: "20", y1: "20", y2: "20", key: "nun2al" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Undo = createLucideIcon("undo", [ ["path", { d: "M3 7v6h6", key: "1v2h90" }], ["path", { d: "M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13", key: "1r6uu6" }] ]); /** * @license lucide-vue-next v0.511.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const X = createLucideIcon("x", [ ["path", { d: "M18 6 6 18", key: "1bl5f8" }], ["path", { d: "m6 6 12 12", key: "d8bk6v" }] ]); const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const _sfc_main$6 = { components: { Bold, Italic, Underline, Strikethrough, AlignLeft, AlignCenter, AlignRight, AlignJustify, Outdent: IndentDecrease, Indent: IndentIncrease, ImageIcon: Image$1, Link, Undo, Redo, X, Table, Settings, SplitSquareVertical: SquareSplitVertical, Combine, Paintbrush }, props: { editor: { type: Object, default: null }, theme: { type: Object, default: () => ({ headerBgColor: '#f3f4f6', headerFgColor: '#111827', contentBgColor: '#ffffff', contentFgColor: '#111827', edgeColor: '#d1d5db', activeButtonBg: '#e5e7eb', activeButtonFg: '#2563eb' }) }, selectedElement: { type: Object, default: null }, selectedCell: { type: Object, default: null } }, emits: ['execute-command', 'insert-image', 'insert-table', 'insert-link', 'edit-table', 'split-cell', 'merge-cells', 'cell-color'], setup(props, { emit }) { const showColorPicker = vue.ref(false); const showBgColorPicker = vue.ref(false); const textColor = vue.ref('#000000'); const bgColor = vue.ref('#ffffff'); const textColorPickerRef = vue.ref(null); const bgColorPickerRef = vue.ref(null); // Track command states in a reactive object const commandStates = vue.reactive({ bold: false, italic: false, underline: false, strikeThrough: false, justifyLeft: false, justifyCenter: false, justifyRight: false, justifyFull: false }); // Update command states const updateCommandStates = () => { try { commandStates.bold = document.queryCommandState('bold'); commandStates.italic = document.queryCommandState('italic'); commandStates.underline = document.queryCommandState('underline'); commandStates.strikeThrough = document.queryCommandState('strikeThrough'); commandStates.justifyLeft = document.queryCommandState('justifyLeft'); commandStates.justifyCenter = document.queryCommandState('justifyCenter'); commandStates.justifyRight = document.queryCommandState('justifyRight'); commandStates.justifyFull = document.queryCommandState('justifyFull'); } catch (e) { console.warn('Error updating command states:', e); } }; // Set up event listeners for selection changes vue.onMounted(() => { document.addEventListener('selectionchange', updateCommandStates); document.addEventListener('click', handleClickOutside); // Initial update updateCommandStates(); }); vue.onUnmounted(() => { document.removeEventListener('selectionchange', updateCommandStates); document.removeEventListener('click', handleClickOutside); }); // Watch for theme changes and update command states vue.watch(() => props.theme, () => { // Force a re-render of active buttons when theme changes updateCommandStates(); }, { deep: true }); const colors = [ '#000000', '#434343', '#666666', '#999999', '#b7b7b7', '#d9d9d9', '#efefef', '#f3f3f3', '#ffffff', '#980000', '#ff0000', '#ff9900', '#ffff00', '#00ff00', '#00ffff', '#4a86e8', '#0000ff', '#9900ff', '#ff00ff', '#e6b8af', '#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#c9daf8', '#cfe2f3', '#d9d2e9', '#ead1dc' ]; // Determine active button background color based on theme const activeButtonBg = vue.computed(() => { // If theme explicitly defines activeButtonBg, use it if (props.theme.activeButtonBg) { return props.theme.activeButtonBg; } // Otherwise, calculate based on the header background color const isDark = isColorDark(props.theme.headerBgColor); if (isDark) { // For dark themes, use a lighter shade for better visibility return lightenColor(props.theme.headerBgColor, 20); } else { // For light themes, use a slightly darker shade return darkenColor(props.theme.headerBgColor, 10); } }); // Determine active icon color based on theme const activeIconColor = vue.computed(() => { // If theme explicitly defines activeButtonFg, use it if (props.theme.activeButtonFg) { return props.theme.activeButtonFg; } // Otherwise, use a blue accent color for active icons return '#2563eb'; // Default blue accent }); // Computed styles based on theme const toolbarStyle = vue.computed(() => ({ backgroundColor: props.theme.headerBgColor, color: props.theme.headerFgColor, borderBottom: `1px solid ${props.theme.edgeColor}` })); const buttonStyle = vue.computed(() => ({ backgroundColor: 'transparent', borderColor: props.theme.edgeColor, color: props.theme.headerFgColor, transition: 'all 0.2s ease' })); const activeButtonStyle = vue.computed(() => { // Ensure we're returning a new object each time to force reactivity return { backgroundColor: activeButtonBg.value, borderColor: props.theme.edgeColor, color: props.theme.headerFgColor, boxShadow: '0 1px 2px rgba(0, 0, 0, 0.1)' }; }); const selectStyle = vue.computed(() => ({ backgroundColor: props.theme.contentBgColor, color: props.theme.contentFgColor, borderColor: props.theme.edgeColor })); const dropdownStyle = vue.computed(() => ({ backgroundColor: props.theme.contentBgColor, color: props.theme.contentFgColor, borderColor: props.theme.edgeColor })); const inputStyle = vue.computed(() => ({ backgroundColor: props.theme.contentBgColor, color: props.theme.contentFgColor, borderColor: props.theme.edgeColor })); const applyButtonStyle = vue.computed(() => ({ backgroundColor: '#2563eb', color: '#ffffff' })); // Function to check if a color is dark const isColorDark = (hexColor) => { // Convert hex to RGB const r = parseInt(hexColor.substring(1, 3), 16); const g = parseInt(hexColor.substring(3, 5), 16); const b = parseInt(hexColor.substring(5, 7), 16); // Calculate brightness (YIQ formula) const brightness = (r * 299 + g * 587 + b * 114) / 1000; // Return true if the color is dark (brightness < 128) return brightness < 128; }; // Function to lighten a color const lightenColor = (hexColor, amount) => { // Convert hex to RGB let r = parseInt(hexColor.substring(1, 3), 16); let g = parseInt(hexColor.substring(3, 5), 16); let b = parseInt(hexColor.substring(5, 7), 16); // Lighten the color r = Math.min(255, r + amount); g = Math.min(255, g + amount); b = Math.min(255, b + amount); // Convert back to hex return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; }; // Function to darken a color const darkenColor = (hexColor, amount) => { // Convert hex to RGB let r = parseInt(hexColor.substring(1, 3), 16); let g = parseInt(hexColor.substring(3, 5), 16); let b = parseInt(hexColor.substring(5, 7), 16); // Darken the color r = Math.max(0, r - amount); g = Math.max(0, g - amount); b = Math.max(0, b - amount); // Convert back to hex return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; }; // Handle clicks outside of color pickers const handleClickOutside = (event) => { // Check if text color picker is open and click is outside if (showColorPicker.value && textColorPickerRef.value && !textColorPickerRef.value.contains(event.target)) { showColorPicker.value = false; } // Check if background color picker is open and click is outside if (showBgColorPicker.value && bgColorPickerRef.value && !bgColorPickerRef.value.contains(event.target)) { showBgColorPicker.value = false; } }; const toggleTextColorPicker = (event) => { // Prevent the click event from propagating to document event.stopPropagation(); // Toggle the color picker showColorPicker.value = !showColorPicker.value; // Close the other color picker if it's open if (showColorPicker.value) { showBgColorPicker.value = false; } }; const toggleBgColorPicker = (event) => { // Prevent the click event from propagating to document event.stopPropagation(); // Toggle the color picker showBgColorPicker.value = !showBgColorPicker.value; // Close the other color picker if it's open if (showBgColorPicker.value) { showColorPicker.value = false; } }; const executeCommand = (command, value = null) => { emit('execute-command', command, value); // Update command states immediately after executing a command setTimeout(updateCommandStates, 10); }; const changeTextColor = (color) => { textColor.value = color; executeCommand('foreColor', color); showColorPicker.value = false; }; const applyTextColor = () => { executeCommand('foreColor', textColor.value); showColorPicker.value = false; }; const changeBackgroundColor = (color) => { bgColor.value = color; executeCommand('hiliteColor', color); showBgColorPicker.value = false; }; const applyBgColor = () => { executeCommand('hiliteColor', bgColor.value); showBgColorPicker.value = false; }; const changeFontSize = (size) => { // Get current selection const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); // If there's a selection, wrap it in a span with the font size if (!selection.isCollapsed) { const content = range.extractContents(); const span = document.createElement('span'); span.style.fontSize = `${size}px`; span.appendChild(content); range.insertNode(span); // Update the selection to include the new span selection.removeAllRanges(); selection.addRange(range); // Emit the command to update the content emit('execute-command', 'insertHTML', span.outerHTML); } else { // If no selection, insert a span at cursor position emit('execute-command', 'insertHTML', `<span style="font-size: ${size}px">&nbsp;</span>`); } // Update command states updateCommandStates(); } }; return { commandStates, showColorPicker, showBgColorPicker, textColor, bgColor, colors, changeTextColor, applyTextColor, changeBackgroundColor, applyBgColor, changeFontSize, textColorPickerRef, bgColorPickerRef, toggleTextColorPicker, toggleBgColorPicker, toolbarStyle, buttonStyle, selectStyle, dropdownStyle, inputStyle, applyButtonStyle, activeButtonStyle, activeButtonBg, activeIconColor, executeCommand }; } }; const _hoisted_1$6 = { class: "relative", ref: "textColorPickerRef" }; const _hoisted_2$6 = { class: "mb-2" }; const _hoisted_3$6 = { class: "grid grid-cols-5 gap-1" }; const _hoisted_4$5 = ["onClick"]; const _hoisted_5$5 = { class: "mt-2 flex justify-end" }; const _hoisted_6$3 = { class: "relative", ref: "bgColorPickerRef" }; const _hoisted_7$2 = { class: "mb-2" }; const _hoisted_8$2 = { class: "grid grid-cols-5 gap-1" }; const _hoisted_9$2 = ["onClick"]; const _hoisted_10$1 = { class: "mt-2 flex justify-end" }; const _hoisted_11$1 = { key: 0, class: "flex items-center" }; const _hoisted_12$1 = { key: 1, class: "flex items-center" }; function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) { const _component_Bold = vue.resolveComponent("Bold"); const _component_Italic = vue.resolveComponent("Italic"); const _component_Underline = vue.resolveComponent("Underline"); const _component_Strikethrough = vue.resolveComponent("Strikethrough"); const _component_AlignLeft = vue.resolveComponent("AlignLeft"); const _component_AlignCenter = vue.resolveComponent("AlignCenter"); const _component_AlignRight = vue.resolveComponent("AlignRight"); const _component_AlignJustify = vue.resolveComponent("AlignJustify"); const _component_Outdent = vue.resolveComponent("Outdent"); const _component_Indent = vue.resolveComponent("Indent"); const _component_ImageIcon = vue.resolveComponent("ImageIcon"); const _component_Table = vue.resolveComponent("Table"); const _component_Settings = vue.resolveComponent("Settings"); const _component_Paintbrush = vue.resolveComponent("Paintbrush"); const _component_Link = vue.resolveComponent("Link"); const _component_Undo = vue.resolveComponent("Undo"); const _component_Redo = vue.resolveComponent("Redo"); const _component_X = vue.resolveComponent("X"); return (vue.openBlock(), vue.createElementBlock("div", { class: "editor-toolbar flex flex-wrap gap-1 p-2", style: vue.normalizeStyle($setup.toolbarStyle) }, [ vue.createElementVNode("select", { class: "h-8 px-2 border rounded text-sm", style: vue.normalizeStyle($setup.selectStyle), onChange: _cache[0] || (_cache[0] = $event => (_ctx.$emit('execute-command', 'fontName', $event.target.value))) }, _cache[28] || (_cache[28] = [ vue.createStaticVNode("<option value=\"\" disabled selected data-v-2f778b9b>Font</option><option value=\"Arial\" data-v-2f778b9b>Arial</option><option value=\"Helvetica\" data-v-2f778b9b>Helvetica</option><option value=\"Times New Roman\" data-v-2f778b9b>Times New Roman</option><option value=\"Courier New\" data-v-2f778b9b>Courier New</option><option value=\"Georgia\" data-v-2f778b9b>Georgia</option><option value=\"Verdana\" data-v-2f778b9b>Verdana</option><option value=\"Impact\" data-v-2f778b9b>Impact</option>", 8) ]), 36), vue.createElementVNode("select", { class: "h-8 px-2 border rounded text-sm", style: vue.normalizeStyle($setup.selectStyle), onChange: _cache[1] || (_cache[1] = $event => ($setup.changeFontSize($event.target.value))) }, _cache[29] || (_cache[29] = [ vue.createStaticVNode("<option value=\"\" disabled selected data-v-2f778b9b>Size</option><option value=\"8\" data-v-2f778b9b>8px</option><option value=\"10\" data-v-2f778b9b>10px</option><option value=\"12\" data-v-2f778b9b>12px</option><option value=\"14\" data-v-2f778b9b>14px</option><option value=\"16\" data-v-2f778b9b>16px</option><option value=\"18\" data-v-2f778b9b>18px</option><option value=\"20\" data-v-2f778b9b>20px</option><option value=\"24\" data-v-2f778b9b>24px</option><option value=\"28\" data-v-2f778b9b>28px</option><option value=\"32\" data-v-2f778b9b>32px</option><option value=\"36\" data-v-2f778b9b>36px</option><option value=\"48\" data-v-2f778b9b>48px</option><option value=\"72\" data-v-2f778b9b>72px</option>", 14) ]), 36), vue.createElementVNode("div", _hoisted_1$6, [ vue.createElementVNode("button", { class: "p-1 rounded flex items-center", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[2] || (_cache[2] = (...args) => ($setup.toggleTextColorPicker && $setup.toggleTextColorPicker(...args))), title: "Text Color" }, [ vue.createElementVNode("span", { class: "w-4 h-4 border rounded-sm", style: vue.normalizeStyle({ backgroundColor: $setup.textColor, borderColor: $props.theme.edgeColor }) }, null, 4) ], 4), ($setup.showColorPicker) ? (vue.openBlock(), vue.createElementBlock("div", { key: 0, class: "absolute top-full left-0 mt-1 p-2 border rounded shadow-lg z-10 w-64", style: vue.normalizeStyle($setup.dropdownStyle) }, [ vue.createElementVNode("div", _hoisted_2$6, [ vue.withDirectives(vue.createElementVNode("input", { type: "text", "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => (($setup.textColor) = $event)), class: "w-full px-2 py-1 border rounded text-sm", style: vue.normalizeStyle($setup.inputStyle), placeholder: "#000000", onKeydown: _cache[4] || (_cache[4] = vue.withKeys((...args) => ($setup.applyTextColor && $setup.applyTextColor(...args)), ["enter"])) }, null, 36), [ [vue.vModelText, $setup.textColor] ]) ]), vue.createElementVNode("div", _hoisted_3$6, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($setup.colors, (color) => { return (vue.openBlock(), vue.createElementBlock("button", { key: color, class: "w-8 h-8 border rounded-sm hover:opacity-80 active:opacity-70", style: vue.normalizeStyle({ backgroundColor: color, borderColor: $props.theme.edgeColor }), onClick: $event => ($setup.changeTextColor(color)) }, null, 12, _hoisted_4$5)) }), 128)) ]), vue.createElementVNode("div", _hoisted_5$5, [ vue.createElementVNode("button", { class: "px-2 py-1 rounded text-sm hover:opacity-90 active:opacity-80", style: vue.normalizeStyle($setup.applyButtonStyle), onClick: _cache[5] || (_cache[5] = (...args) => ($setup.applyTextColor && $setup.applyTextColor(...args))) }, " Apply ", 4) ]) ], 4)) : vue.createCommentVNode("", true) ], 512), vue.createElementVNode("div", _hoisted_6$3, [ vue.createElementVNode("button", { class: "p-1 rounded flex items-center", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[6] || (_cache[6] = (...args) => ($setup.toggleBgColorPicker && $setup.toggleBgColorPicker(...args))), title: "Background Color" }, [ vue.createElementVNode("span", { class: "w-4 h-4 border rounded-sm", style: vue.normalizeStyle({ backgroundColor: $setup.bgColor, borderColor: $props.theme.edgeColor }) }, null, 4) ], 4), ($setup.showBgColorPicker) ? (vue.openBlock(), vue.createElementBlock("div", { key: 0, class: "absolute top-full left-0 mt-1 p-2 border rounded shadow-lg z-10 w-64", style: vue.normalizeStyle($setup.dropdownStyle) }, [ vue.createElementVNode("div", _hoisted_7$2, [ vue.withDirectives(vue.createElementVNode("input", { type: "text", "onUpdate:modelValue": _cache[7] || (_cache[7] = $event => (($setup.bgColor) = $event)), class: "w-full px-2 py-1 border rounded text-sm", style: vue.normalizeStyle($setup.inputStyle), placeholder: "#ffffff", onKeydown: _cache[8] || (_cache[8] = vue.withKeys((...args) => ($setup.applyBgColor && $setup.applyBgColor(...args)), ["enter"])) }, null, 36), [ [vue.vModelText, $setup.bgColor] ]) ]), vue.createElementVNode("div", _hoisted_8$2, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($setup.colors, (color) => { return (vue.openBlock(), vue.createElementBlock("button", { key: color, class: "w-8 h-8 border rounded-sm hover:opacity-80 active:opacity-70", style: vue.normalizeStyle({ backgroundColor: color, borderColor: $props.theme.edgeColor }), onClick: $event => ($setup.changeBackgroundColor(color)) }, null, 12, _hoisted_9$2)) }), 128)) ]), vue.createElementVNode("div", _hoisted_10$1, [ vue.createElementVNode("button", { class: "px-2 py-1 rounded text-sm hover:opacity-90 active:opacity-80", style: vue.normalizeStyle($setup.applyButtonStyle), onClick: _cache[9] || (_cache[9] = (...args) => ($setup.applyBgColor && $setup.applyBgColor(...args))) }, " Apply ", 4) ]) ], 4)) : vue.createCommentVNode("", true) ], 512), vue.createElementVNode("div", { class: "h-8 w-px mx-1", style: vue.normalizeStyle({ backgroundColor: $props.theme.edgeColor }) }, null, 4), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.bold }]), style: vue.normalizeStyle($setup.commandStates.bold ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[10] || (_cache[10] = $event => ($setup.executeCommand('bold'))), title: "Bold" }, [ vue.createVNode(_component_Bold, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.bold ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.italic }]), style: vue.normalizeStyle($setup.commandStates.italic ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[11] || (_cache[11] = $event => ($setup.executeCommand('italic'))), title: "Italic" }, [ vue.createVNode(_component_Italic, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.italic ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.underline }]), style: vue.normalizeStyle($setup.commandStates.underline ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[12] || (_cache[12] = $event => ($setup.executeCommand('underline'))), title: "Underline" }, [ vue.createVNode(_component_Underline, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.underline ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.strikeThrough }]), style: vue.normalizeStyle($setup.commandStates.strikeThrough ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[13] || (_cache[13] = $event => ($setup.executeCommand('strikeThrough'))), title: "Strike Through" }, [ vue.createVNode(_component_Strikethrough, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.strikeThrough ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("div", { class: "h-8 w-px mx-1", style: vue.normalizeStyle({ backgroundColor: $props.theme.edgeColor }) }, null, 4), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.justifyLeft }]), style: vue.normalizeStyle($setup.commandStates.justifyLeft ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[14] || (_cache[14] = $event => ($setup.executeCommand('justifyLeft'))), title: "Align Left" }, [ vue.createVNode(_component_AlignLeft, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.justifyLeft ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.justifyCenter }]), style: vue.normalizeStyle($setup.commandStates.justifyCenter ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[15] || (_cache[15] = $event => ($setup.executeCommand('justifyCenter'))), title: "Align Center" }, [ vue.createVNode(_component_AlignCenter, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.justifyCenter ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.justifyRight }]), style: vue.normalizeStyle($setup.commandStates.justifyRight ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[16] || (_cache[16] = $event => ($setup.executeCommand('justifyRight'))), title: "Align Right" }, [ vue.createVNode(_component_AlignRight, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.justifyRight ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("button", { class: vue.normalizeClass(["p-1 rounded", { 'editor-button-active': $setup.commandStates.justifyFull }]), style: vue.normalizeStyle($setup.commandStates.justifyFull ? $setup.activeButtonStyle : $setup.buttonStyle), onClick: _cache[17] || (_cache[17] = $event => ($setup.executeCommand('justifyFull'))), title: "Justify" }, [ vue.createVNode(_component_AlignJustify, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $setup.commandStates.justifyFull ? $setup.activeIconColor : $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 6), vue.createElementVNode("div", { class: "h-8 w-px mx-1", style: vue.normalizeStyle({ backgroundColor: $props.theme.edgeColor }) }, null, 4), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[18] || (_cache[18] = $event => ($setup.executeCommand('outdent'))), title: "Decrease Indent" }, [ vue.createVNode(_component_Outdent, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[19] || (_cache[19] = $event => ($setup.executeCommand('indent'))), title: "Increase Indent" }, [ vue.createVNode(_component_Indent, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4), vue.createElementVNode("div", { class: "h-8 w-px mx-1", style: vue.normalizeStyle({ backgroundColor: $props.theme.edgeColor }) }, null, 4), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[20] || (_cache[20] = $event => (_ctx.$emit('insert-image'))), title: "Insert Image" }, [ vue.createVNode(_component_ImageIcon, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[21] || (_cache[21] = $event => (_ctx.$emit('insert-table'))), title: "Insert Table" }, [ vue.createVNode(_component_Table, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4), ($props.selectedElement && $props.selectedElement.tagName === 'TABLE') ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11$1, [ vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[22] || (_cache[22] = $event => (_ctx.$emit('edit-table'))), title: "Edit Table" }, [ vue.createVNode(_component_Settings, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4) ])) : vue.createCommentVNode("", true), ($props.selectedCell && ($props.selectedCell.tagName === 'TD' || $props.selectedCell.tagName === 'TH')) ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_12$1, [ vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[23] || (_cache[23] = $event => (_ctx.$emit('cell-color'))), title: "Cell Background Color" }, [ vue.createVNode(_component_Paintbrush, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4) ])) : vue.createCommentVNode("", true), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[24] || (_cache[24] = $event => (_ctx.$emit('insert-link'))), title: "Insert Link" }, [ vue.createVNode(_component_Link, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4), vue.createElementVNode("div", { class: "h-8 w-px mx-1", style: vue.normalizeStyle({ backgroundColor: $props.theme.edgeColor }) }, null, 4), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[25] || (_cache[25] = $event => ($setup.executeCommand('undo'))), title: "Undo" }, [ vue.createVNode(_component_Undo, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[26] || (_cache[26] = $event => ($setup.executeCommand('redo'))), title: "Redo" }, [ vue.createVNode(_component_Redo, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4), vue.createElementVNode("div", { class: "h-8 w-px mx-1", style: vue.normalizeStyle({ backgroundColor: $props.theme.edgeColor }) }, null, 4), vue.createElementVNode("button", { class: "p-1 rounded", style: vue.normalizeStyle($setup.buttonStyle), onClick: _cache[27] || (_cache[27] = $event => ($setup.executeCommand('removeFormat'))), title: "Clear Formatting" }, [ vue.createVNode(_component_X, { class: "h-4 w-4", style: vue.normalizeStyle({ color: $props.theme.headerFgColor }) }, null, 8, ["style"]) ], 4) ], 4)) } const EditorToolbar = /*#__PURE__*/_export_sfc(_sfc_main$6, [['render',_sfc_render$6],['__scopeId',"data-v-2f778b9b"]]); const _sfc_main$5 = { props: { theme: { type: Object, default: () => ({ headerBgColor: '#f3f4f6', headerFgColor: '#111827', contentBgColor: '#ffffff', contentFgColor: '#111827', edgeColor: '#d1d5db' }) } }, emits: ['close', 'insert'], setup(props, { emit }) { const imageUrl = vue.ref(''); // Computed styles based on theme const dialogStyle = vue.computed(() => ({ backgroundColor: props.theme.contentBgColor, color: props.theme.contentFgColor, border: `1px solid ${props.theme.edgeColor}`, fontFamily: 'inherit' })); const headerStyle = vue.computed(() => ({ backgroundColor: props.theme.headerBgColor, color: props.theme.headerFgColor, padding: '8px', margin: '-24px -24px 16px -24px', borderTopLeftRadius: '0.5rem', borderTopRightRadius: '0.5rem', borderBottom: `1px solid ${props.theme.edgeColor}` })); const inputStyle = vue.computed(() => ({ backgroundColor: props.theme.contentBgColor, color: props.theme.contentFgColor, borderColor: props.theme.edgeColor })); const cancelButtonStyle = vue.computed(() => ({ backgroundColor: '#e5e7eb', color: '#111827' })); const confirmButtonStyle = vue.computed(() => ({ backgroundColor: '#2563eb', color: '#ffffff' })); const insertImage = () => { if (imageUrl.value) { emit('insert', imageUrl.value); } }; return { imageUrl, insertImage, dialogStyle, headerStyle, inputStyle, cancelButtonStyle, confirmButtonStyle }; } }; const _hoisted_1$5 = { class: "mb-4" }; const _hoisted_2$5 = { class: "flex justify-end space-x-2" }; const _hoisted_3$5 = ["disabled"]; function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) { return (vue.openBlock(), vue.createElementBlock("div", { class: "rounded-lg p-6 w-full max-w-md shadow-lg", style: vue.normalizeStyle($setup.dialogStyle) }, [ vue.createElementVNode("h3", { class: "text-lg font-medium mb-4", style: vue.normalizeStyle($setup.headerStyle) }, "Insert Image", 4), vue.createElementVNode("div", _hoisted_1$5, [ vue.createElementVNode("label", { class: "block text-sm font-medium mb-1", style: vue.normalizeStyle({ color: $props.theme.contentFgColor }) }, "Image URL:", 4), vue.withDirectives(vue.createElementVNode("input", { type: "text", "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (($setup.imageUrl) = $event)), class: "w-full px-3 py-2 border rounded-md", style: vue.normalizeStyle($setup.inputStyle), placeholder: "https://example.com/image.jpg", onKeydown: _cache[1] || (_cache[1] = vue.withKeys((...args) => ($setup.insertImage && $setup.insertImage(...args)), ["enter"])) }, null, 36), [ [vue.vModelText, $setup.imageUrl] ]) ]), vue.createElementVNode("div", _hoisted_2$5, [ vue.createElementVNode("button", { class: "px-4 py-2 rounded hover:opacity-90 active:opacity-80", style: vue.normalizeStyle($setup.cancelButtonStyle), onClick: _cache[2] || (_cache[2] = $event => (_ctx.$emit('close')))