Files
myprojplanet_vite/src/components/CMyEditor/CMyEditor.ts
Surya Paolo a51bc5a8a2 - aggiornamenti guida RIS, FAQ
- Editor HTML aggiunto CSS e Script
- Statistiche
- CRISBalanceBar
- Inizio Sync... (ma disattivato)
2025-12-02 22:16:24 +01:00

328 lines
8.9 KiB
TypeScript
Executable File

import { tools } from '@tools';
import { CTitleBanner } from '../CTitleBanner';
import { defineComponent, onMounted, onUnmounted, ref, toRef, watch } from 'vue';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
export default defineComponent({
name: 'CMyEditor',
components: { CTitleBanner },
props: {
title: {
type: String,
required: false,
default: '',
},
value: {
type: String,
required: false,
default: '',
},
myclass: {
type: String,
required: false,
default: '',
},
customStyles: {
type: String,
required: false,
default: '',
},
showButtons: {
type: Boolean,
required: false,
default: true,
},
canModify: {
type: Boolean,
required: false,
default: false,
},
hideTools: {
type: Boolean,
required: false,
default: false,
},
maxlength: {
type: Number,
required: false,
default: 0,
},
startInCodeMode: {
type: Boolean,
required: false,
default: false,
},
},
setup(props, { emit }) {
const $q = useQuasar();
const { t } = useI18n();
const editorRef = ref(<any>null);
const editor = ref('');
const characterCount = ref(0);
//const myvalue = toRef(props, 'value')
const myvalue = ref('');
const mycolor = ref('');
const editorId = ref(
`editor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
);
const styleElement = ref<HTMLStyleElement | null>(null);
// Watch per applicare stili personalizzati
watch(
() => props.customStyles,
(newStyles) => {
if (newStyles && editorRef.value) {
applyCustomStyles(newStyles);
}
}
);
const myfonts = ref({
arial: 'Arial',
arial_black: 'Arial Black',
AGaramondProRegular: 'AGaramondPro-Regular',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
});
const showtools = ref(false);
const toolbarcomp = ref([
['left', 'center', 'right', 'justify'],
['bold', 'italic', 'underline', 'strike'],
['token', 'hr', 'link', 'custom_btn', 'print', 'fullscreen'],
[
{
label: $q.lang.editor.formatting,
icon: $q.iconSet.editor.formatting,
list: 'no-icons',
options: ['p', 'h4', 'h5', 'h6', 'code'],
},
{
label: $q.lang.editor.fontSize,
icon: $q.iconSet.editor.fontSize,
fixedLabel: true,
fixedIcon: true,
list: 'no-icons',
options: ['size-1', 'size-2', 'size-3', 'size-4', 'size-5', 'size-6', 'size-7'],
},
{
label: $q.lang.editor.defaultFont,
icon: $q.iconSet.editor.font,
fixedIcon: true,
list: 'no-icons',
options: [
'default_font',
'arial',
'arial_black',
'comic_sans',
'courier_new',
'impact',
'lucida_grande',
'times_new_roman',
'verdana',
],
},
'removeFormat',
],
['quote', 'unordered', 'ordered', 'outdent', 'indent'],
['undo', 'redo', 'viewsource'],
]);
watch(
() => props.value,
(newval, oldval) => {
if (props.value === undefined) myvalue.value = '';
else myvalue.value = props.value;
}
);
// Funzione per applicare stili custom
// ALTERNATIVA: Se vuoi applicare gli stili solo al contenuto dell'editor (più sicuro)
function applyCustomStyles(styles: string) {
// console.log('Applying custom styles:', styles);
// Rimuovi style precedente
if (styleElement.value && styleElement.value.parentNode) {
styleElement.value.parentNode.removeChild(styleElement.value);
}
if (!styles) return;
// Crea style element nel HEAD
styleElement.value = document.createElement('style');
styleElement.value.setAttribute('type', 'text/css');
styleElement.value.setAttribute('data-editor-styles', editorId.value);
// CSS con selettori super specifici usando l'ID univoco
const css = `
[data-editor-id="${editorId.value}"] .q-editor__content {
${styles}
}
[data-editor-id="${editorId.value}"] .q-editor__content * {
${styles}
}
/* Selettori per classi custom nel contenuto */
[data-editor-id="${editorId.value}"] .q-editor__content .prova1 {
color: red !important;
}
`;
styleElement.value.textContent = css;
document.head.appendChild(styleElement.value);
// console.log('Style injected:', css);
}
function getTextLength(html: string) {
// Crea un elemento temporaneo per convertire HTML in testo
const div = document.createElement('div');
div.innerHTML = html; // Imposta l'HTML
return div.innerText.length; // Restituisce la lunghezza del testo
}
function changeval(newval: any) {
// console.log('myEditor: changeval', newval)
characterCount.value = getTextLength(newval);
// newval = newval.replace(/&nbsp;/g, ' ')
emit('update:value', newval);
}
function annulla() {
emit('annulla', true);
}
function saveval() {
// Converti i <b> in <strong>
myvalue.value = tools.convertinbspInSpazi(myvalue.value);
// myvalue.value = tools.convertiTagHTMLPerBOT(myvalue.value)
console.log('saveval', myvalue.value);
emit('showandsave', myvalue.value);
// emit('update:value', myvalue)
}
function setcolor() {
document.execCommand('foreColor', false, mycolor.value);
}
/**
* Capture the <CTL-V> paste event, only allow plain-text, no images.
*
* see: https://stackoverflow.com/a/28213320
*
* @param {object} evt - array of files
* @author Daniel Thompson-Yvetot
* @license MIT
*/
function pasteCapture(evt: any) {
// let text, onPasteStripFormattingIEPaste
// evt.preventDefault()
// if (evt.originalEvent && evt.originalEvent.clipboardData.getData) {
// text = evt.originalEvent.clipboardData.getData('text/plain')
// $refs.editor_ref.runCmd('insertText', text)
// }
// else if (evt.clipboardData && evt.clipboardData.getData) {
// text = evt.clipboardData.getData('text/plain')
// $refs.editor_ref.runCmd('insertText', text)
// }
// else if (window.clipboardData && window.clipboardData.getData) {
// if (!onPasteStripFormattingIEPaste) {
// onPasteStripFormattingIEPaste = true
// $refs.editor_ref.runCmd('ms-pasteTextOnly', text)
// }
// onPasteStripFormattingIEPaste = false
// }
}
function mounted() {
if (props.value === undefined) myvalue.value = '';
else myvalue.value = props.value;
showtools.value = tools.getCookie('showtools', '0') === '1';
characterCount.value = getTextLength(myvalue.value);
if (props.customStyles) {
setTimeout(() => {
applyCustomStyles(props.customStyles);
}, 300);
}
if (props.startInCodeMode) {
// Attiva modalità codice di default
setTimeout(() => {
if (editorRef.value) {
editorRef.value.runCmd('viewsource');
}
}, 100);
}
}
function onPaste(evt: any) {
// Let inputs do their thing, so we don't break pasting of links.
if (evt.target.nodeName === 'INPUT') return;
let text, onPasteStripFormattingIEPaste;
evt.preventDefault();
evt.stopPropagation();
if (evt.originalEvent && evt.originalEvent.clipboardData.getData) {
text = evt.originalEvent.clipboardData.getData('text/plain');
editorRef.value.runCmd('insertText', text);
} else if (evt.clipboardData && evt.clipboardData.getData) {
text = evt.clipboardData.getData('text/plain');
editorRef.value.runCmd('insertText', text);
}
/*else if (ClipboardEvent.clipboardData && ClipboardEvent.clipboardData.getData) {
if (!onPasteStripFormattingIEPaste) {
onPasteStripFormattingIEPaste = true
editorRef.value.runCmd('ms-pasteTextOnly', text)
}
onPasteStripFormattingIEPaste = false
}*/
}
onMounted(mounted);
// Cleanup quando componente viene distrutto
onUnmounted(() => {
if (styleElement.value && styleElement.value.parentNode) {
styleElement.value.parentNode.removeChild(styleElement.value);
}
});
return {
myfonts,
toolbarcomp,
editor,
myvalue,
mycolor,
changeval,
annulla,
saveval,
setcolor,
pasteCapture,
tools,
onPaste,
editorRef,
showtools,
characterCount,
t,
applyCustomStyles,
editorId,
};
},
});