mirror of
https://github.com/webislife/wc-wysiwyg.git
synced 2025-07-22 06:59:36 +00:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
ef66238fec | |||
071e8f8eee | |||
bff5b6befa | |||
8ae8da7c4a | |||
d12b5a55c8 | |||
9e9b0d75fd | |||
fa51c1ba5f | |||
bc2926ce61 | |||
9f597ce45a | |||
26f9927437 |
@ -40,6 +40,10 @@ See full demo - [wc-wysiwyg demo](https://webislife.ru/demo/wc-wysiwyg/) list an
|
||||
|
||||
✅ Inserting `<video>` element
|
||||
|
||||
- ✅ Suppoer extensions
|
||||
- Color text and background editor
|
||||
- Emoji table
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
|
2
dist/core/el.js
vendored
2
dist/core/el.js
vendored
@ -1 +1 @@
|
||||
export const el=(tagName,{classList,styles,props,attrs,options,append}={})=>{if(!tagName)throw new Error(`Undefined tag ${tagName}`);const element=document.createElement(tagName,options);if(classList)for(let i=0;i<classList.length;i++){const styleClass=classList[i];element.classList.add(styleClass)}if(styles){const stylesKeys=Object.keys(styles);for(let i=0;i<stylesKeys.length;i++){const key=stylesKeys[i];element.style[key]=styles[key]}}if(props){const propKeys=Object.keys(props);for(let i=0;i<propKeys.length;i++){const key=propKeys[i];element[key]=props[key]}}if(attrs){const attrsKeys=Object.keys(attrs);for(let i=0;i<attrsKeys.length;i++){const key=attrsKeys[i];attrs[key]&&element.setAttribute(key,attrs[key])}}if(append)for(let i=0;i<append.length;i++){const appendEl=append[i];element.append(appendEl)}return element};
|
||||
export const el=(tagName,{classList,styles,props,attrs,options,append}={})=>{if(!tagName)throw new Error(`Undefined tag ${tagName}`);const element=document.createElement(tagName,options);if(classList)for(let i=0;i<classList.length;i++)classList[i]&&element.classList.add(classList[i]);if(styles&&Object.assign(element.style,styles),props){const propKeys=Object.keys(props);for(let i=0;i<propKeys.length;i++){const key=propKeys[i];element[key]=props[key]}}if(attrs){const attrsKeys=Object.keys(attrs);for(let i=0;i<attrsKeys.length;i++){const key=attrsKeys[i];attrs[key]&&element.setAttribute(key,attrs[key])}}return append&&element.append(...append),element};
|
2
dist/core/translates.js
vendored
2
dist/core/translates.js
vendored
File diff suppressed because one or more lines are too long
12
dist/sass/content.css
vendored
12
dist/sass/content.css
vendored
@ -75,19 +75,10 @@ h5 {
|
||||
h6 {
|
||||
font-size: 1em; }
|
||||
|
||||
/* h1[id]::before,
|
||||
h2[id]::before,
|
||||
h3[id]::before,
|
||||
h4[id]::before,
|
||||
h5[id]::before,
|
||||
h6[id]::before {
|
||||
content: '§';
|
||||
color: var(--color-blue-gray-300);
|
||||
margin-right: 0.5em;
|
||||
} */
|
||||
/* del\ins */
|
||||
del {
|
||||
color: var(--color-red-900);
|
||||
border-bottom: 1px solid var(--color-red-900);
|
||||
background-color: var(--color-red-50);
|
||||
text-decoration: none; }
|
||||
|
||||
@ -104,6 +95,7 @@ del:before {
|
||||
|
||||
ins {
|
||||
color: var(--color-green-900);
|
||||
border-bottom: 1px solid var(--color-green-900);
|
||||
background-color: var(--color-green-50);
|
||||
text-decoration: none; }
|
||||
|
||||
|
2
dist/wc-wysiwyg.js
vendored
2
dist/wc-wysiwyg.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@webislife/wc-wysiwyg",
|
||||
"version": "0.9.32",
|
||||
"version": "1.0.1",
|
||||
"description": "WYWSIWYG HTML5 Editor written in ts and designed by web-componennt, support all JS frameworks and browsers",
|
||||
"main": "dist/wc-wysiwyg.js",
|
||||
"type": "module",
|
||||
|
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Short
|
||||
* Short for document.createElement
|
||||
* @param tagName element tag name
|
||||
* @param params list of object params for document.createElements
|
||||
* @returns
|
||||
* @returns HTML\CUSTOMElement
|
||||
*/
|
||||
export const el = (tagName:keyof HTMLElementTagNameMap|string, {classList, styles, props, attrs, options, append}:{
|
||||
classList?: string[],
|
||||
@ -21,17 +21,14 @@
|
||||
// element.classList
|
||||
if(classList) {
|
||||
for (let i = 0; i < classList.length; i++) {
|
||||
const styleClass = classList[i];
|
||||
element.classList.add(styleClass)
|
||||
if(classList[i]){
|
||||
element.classList.add(classList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// element.style[prop]
|
||||
if(styles) {
|
||||
const stylesKeys = Object.keys(styles);
|
||||
for (let i = 0; i < stylesKeys.length; i++) {
|
||||
const key = stylesKeys[i];
|
||||
element.style[key] = styles[key];
|
||||
}
|
||||
Object.assign(element.style, styles);
|
||||
}
|
||||
// element[prop]
|
||||
if(props) {
|
||||
@ -51,11 +48,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
//append child elements
|
||||
if(append) {
|
||||
for (let i = 0; i < append.length; i++) {
|
||||
const appendEl = append[i];
|
||||
element.append(appendEl);
|
||||
}
|
||||
element.append(...append);
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
220
src/extensions/colorerDialog.ts
Normal file
220
src/extensions/colorerDialog.ts
Normal file
@ -0,0 +1,220 @@
|
||||
import { el } from '../core/el.js';
|
||||
import WCWYSIWYG from '../wc-wysiwyg.js';
|
||||
//Extnesion translates
|
||||
const t = {
|
||||
ru: {
|
||||
bg: 'Фон',
|
||||
text: 'Текст',
|
||||
bgColor: 'Цвет фона',
|
||||
textColor: 'Цвет текста',
|
||||
},
|
||||
en: {
|
||||
bg: 'Background',
|
||||
text: 'Text',
|
||||
bgColor: 'Background color',
|
||||
textColor: 'Text color',
|
||||
}
|
||||
};
|
||||
const _t = (key:string, lang = navigator.language):string => t[lang] ? t[lang][key] || "-" : t["en"][key];
|
||||
const Colors = {
|
||||
red: ['FFEBEE', 'FFCDD2', 'EF9A9A', 'E57373', 'EF5350', 'F44336', 'E53935', 'D32F2F', 'C62828', 'B71C1C'],
|
||||
pink: ['FCE4EC', 'F8BBD0', 'F48FB1', 'F06292', 'EC407A', 'E91E63', 'D81B60', 'C2185B', 'AD1457', '880E4F'],
|
||||
purple: ['F3E5F5', 'E1BEE7', 'CE93D8', 'BA68C8', 'AB47BC', '9C27B0', '8E24AA', '7B1FA2', '6A1B9A', '4A148C'],
|
||||
// deepPurple: ['EDE7F6', 'D1C4E9', 'B39DDB', '9575CD', '7E57C2', '673AB7', '5E35B1', '512DA8', '4527A0', '311B92'],
|
||||
indigo: ['E8EAF6', 'C5CAE9', '9FA8DA', '7986CB', '5C6BC0', '3F51B5', '3949AB', '303F9F', '283593', '1A237E'],
|
||||
blue: ['E3F2FD', 'BBDEFB', '90CAF9', '64B5F6', '42A5F5', '2196F3', '1E88E5', '1976D2', '1565C0', '0D47A1'],
|
||||
// lightBlue: ['E1F5FE', 'B3E5FC', '81D4FA', '4FC3F7', '29B6F6', '03A9F4', '039BE5', '0288D1', '0277BD', '01579B'],
|
||||
cyan: ['E0F7FA', 'B2EBF2', '80DEEA', '4DD0E1', '26C6DA', '00BCD4', '00ACC1', '0097A7', '00838F', '006064'],
|
||||
teal: ['E0F2F1', 'B2DFDB', '80CBC4', '4DB6AC', '26A69A', '009688', '00897B', '00796B', '00695C', '004D40'],
|
||||
green: ['E8F5E9', 'C8E6C9', 'A5D6A7', '81C784', '66BB6A', '4CAF50', '43A047', '388E3C', '2E7D32', '1B5E20'],
|
||||
// lightGreen: ['F1F8E9', 'DCEDC8', 'C5E1A5', 'AED581', '9CCC65', '8BC34A', '7CB342', '689F38', '558B2F', '33691E'],
|
||||
lime: ['FFFDE7', 'FFF9C4', 'FFF59D', 'FFF176', 'FFEE58', 'FFEB3B', 'FDD835', 'FBC02D', 'F9A825', 'F57F17'],
|
||||
yellow: ['FFF8E1', 'FFECB3', 'FFE082', 'FFD54F', 'FFCA28', 'FFC107', 'FFB300', 'FFA000', 'FF8F00', 'FF6F00'],
|
||||
// amber: ['FFF3E0', 'FFE0B2', 'FFCC80', 'FFB74D', 'FFA726', 'FF9800', 'FB8C00', 'F57C00', 'EF6C00', 'E65100'],
|
||||
orange: ['FBE9E7', 'FFCCBC', 'FFAB91', 'FF8A65', 'FF7043', 'FF5722', 'F4511E', 'E64A19', 'D84315', 'BF360C'],
|
||||
// deepOrange: ['EFEBE9', 'D7CCC8', 'BCAAA4', 'A1887F', '8D6E63', '795548', '6D4C41', '5D4037', '4E342E', '3E2723'],
|
||||
brown: ['EFEBE9', 'D7CCC8', 'BCAAA4', 'A1887F', '8D6E63', '795548', '6D4C41', '5D4037', '4E342E', '3E2723'],
|
||||
grey:['FAFAFA', 'F5F5F5', 'EEEEEE', 'E0E0E0', 'BDBDBD', '9E9E9E', '757575', '616161', '424242', '212121'],
|
||||
blueGrey: ['ECEFF1', 'CFD8DC', 'B0BEC5', '90A4AE', '78909C', '607D8B', '546E7A', '455A64', '37474F', '263238'],
|
||||
};
|
||||
|
||||
class WCWYSIWYGExtensionColorerDialog {
|
||||
WCWYSIWYG:WCWYSIWYG
|
||||
ColorerText:HTMLButtonElement
|
||||
ColorerBackground:HTMLButtonElement
|
||||
ColorerDialogLabel:HTMLLabelElement
|
||||
ColorerDialogColorInput:HTMLInputElement
|
||||
Dialog:HTMLDialogElement|null = null
|
||||
ActiveColors:{
|
||||
text:string|null,
|
||||
bg:string|null,
|
||||
}
|
||||
ColorerTarget: HTMLElement
|
||||
constructor(WYSIWYG) {
|
||||
this.WCWYSIWYG = WYSIWYG;
|
||||
this.ActiveColors = new Proxy({
|
||||
text: null,
|
||||
bg: null,
|
||||
}, {
|
||||
get(target,prop) {
|
||||
return target[prop]
|
||||
},
|
||||
set: (target, prop, value) => {
|
||||
console.log('set color', value, prop);
|
||||
this.ColorerDialogColorInput.value = value;
|
||||
target[prop] = value;
|
||||
if(prop == 'text') {
|
||||
this.ColorerTarget.style.color = value;
|
||||
this.ColorerText.setAttribute('style', `--colorer:${value}`);
|
||||
} else {
|
||||
this.ColorerTarget.style.backgroundColor = value;
|
||||
this.ColorerBackground.setAttribute('style', `--colorer:${value}`);
|
||||
}
|
||||
return true
|
||||
}
|
||||
});
|
||||
console.log('colorer constructor', this);
|
||||
}
|
||||
connectedCallback() {
|
||||
console.log('colorer connectedCallback');
|
||||
this.ColorerText = el('button', {
|
||||
classList: ['wc-wysiwyg_btn', '-prevcolor'],
|
||||
props: {
|
||||
innerHTML: _t('text'),
|
||||
onpointerup: () => this.#showDialog('text'),
|
||||
},
|
||||
attrs: {
|
||||
"data-hint": _t('textColor')
|
||||
},
|
||||
});
|
||||
this.ColorerBackground = el('button', {
|
||||
classList: ['wc-wysiwyg_btn', '-prevcolor'],
|
||||
props: {
|
||||
innerHTML: _t('bg'),
|
||||
onpointerup: () => this.#showDialog('bg'),
|
||||
},
|
||||
attrs: {
|
||||
"data-hint": _t('bgColor')
|
||||
},
|
||||
});
|
||||
this.ColorerDialogLabel = el('label', {
|
||||
props: {
|
||||
innerText: ''
|
||||
}
|
||||
});
|
||||
this.ColorerDialogColorInput = el('input', {
|
||||
props: {
|
||||
type: 'color',
|
||||
value: '#fffccc'
|
||||
}
|
||||
});
|
||||
this.WCWYSIWYG.addEventListener('editprops', (event:CustomEvent) => {
|
||||
if(event.detail.eventTarget) {
|
||||
const target = event.detail.eventTarget;
|
||||
console.log('check target', target);
|
||||
|
||||
this.ColorerTarget = target;
|
||||
this.ActiveColors.text = this.#rgbToHex(target.style.color);
|
||||
this.ActiveColors.bg = this.#rgbToHex(target.style.backgroundColor);
|
||||
|
||||
// this.ColorerText.setAttribute('data-color', value);
|
||||
// this.ColorerBackground.setAttribute('data-color', value);
|
||||
|
||||
}
|
||||
});
|
||||
this.WCWYSIWYG.EditorActionsSection.append(
|
||||
this.ColorerText,
|
||||
this.ColorerBackground
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Converts a rgb string to hex string
|
||||
* @param {String} rgbString - A string of rgb values (e.g. "255, 0, 128")
|
||||
* @return {String} A hex string in the format of #RRGGBB (e.g. "#FF0080")
|
||||
*/
|
||||
#rgbToHex(rgbString) {
|
||||
const arrRgb = rgbString.match(/([0-9]{1,3})/gm);
|
||||
if(arrRgb === null || arrRgb.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
const r = +arrRgb[0];
|
||||
const g = +arrRgb[1];
|
||||
const b = +arrRgb[2];
|
||||
return "#" + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1);
|
||||
}
|
||||
#showDialog(colorType:string) {
|
||||
console.log('show dialog');
|
||||
if(this.Dialog === null) {
|
||||
this.Dialog = el('dialog', {
|
||||
classList: ['wc-wysiwyg_dialog', '-modal', '-colors'],
|
||||
props: {
|
||||
innerHTML: (() => {
|
||||
let html = '';
|
||||
for(let colorName in Colors) {
|
||||
html += `<fieldset class="-palette">`;
|
||||
for (let i = 0; i < Colors[colorName].length; i++) {
|
||||
const hexColor = Colors[colorName][i];
|
||||
html += `<button class="wc-wysiwyg_btn -color" data-color="#${hexColor}" style="background-color: #${hexColor};"></button>`;
|
||||
}
|
||||
html += `</fieldset>`;
|
||||
}
|
||||
return html
|
||||
})(),
|
||||
onpointerup: event => {
|
||||
if(event.target.classList.contains('-color')) {
|
||||
this.ActiveColors[colorType] = event.target.getAttribute('data-color');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
append: [
|
||||
el('fieldset', {
|
||||
styles: {
|
||||
background: 'var(--color-blue-gray-50)',
|
||||
outline: '0',
|
||||
display: 'flex',
|
||||
border: '0',
|
||||
marginTop: '10px',
|
||||
borderRadius: '0.5em',
|
||||
width: '100%',
|
||||
},
|
||||
append: [
|
||||
|
||||
this.ColorerDialogLabel,
|
||||
this.ColorerDialogColorInput,
|
||||
el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
props: {
|
||||
type: 'button',
|
||||
innerText: 'OK',
|
||||
onpointerup: event => event.target.closest('dialog').close(),
|
||||
}
|
||||
}),
|
||||
el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
props: {
|
||||
type: 'button',
|
||||
innerText: 'Clear',
|
||||
onpointerup: event => this.ColorerDialogColorInput.value = null
|
||||
}
|
||||
}),
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
this.WCWYSIWYG.EditorActionsSection.append(this.Dialog);
|
||||
|
||||
// this.WCWYSIWYG.EditorInlineDialog.append(this.Dialog);
|
||||
}
|
||||
this.Dialog.onpointerup = event => {
|
||||
const target = event.target as HTMLElement;
|
||||
if(target.classList.contains('-color')) {
|
||||
this.ActiveColors[colorType] = target.getAttribute('data-color');
|
||||
}
|
||||
};
|
||||
this.ColorerDialogLabel.innerText = _t(colorType);
|
||||
this.Dialog.showModal();
|
||||
}
|
||||
}
|
||||
|
||||
export default WCWYSIWYGExtensionColorerDialog;
|
100
src/extensions/emojiDialog.ts
Normal file
100
src/extensions/emojiDialog.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { el } from '../core/el.js';
|
||||
|
||||
//Extnesion translates
|
||||
const t = {
|
||||
ru: {
|
||||
emoji: 'Смайлики',
|
||||
action: 'Нажмите чтобы скопировать',
|
||||
emoticons: 'Эмоции',
|
||||
dingbats: 'Значки',
|
||||
map: 'Транспорт и карты',
|
||||
additional: 'Дополнительные',
|
||||
},
|
||||
en: {
|
||||
emoji: 'Emoji',
|
||||
action: 'Click to copy',
|
||||
emoticons: 'Emoticons',
|
||||
dingbats: 'Dsingbats',
|
||||
map: 'Transport and map',
|
||||
additional: 'Other additional',
|
||||
}
|
||||
};
|
||||
const _t = (key:string, lang = navigator.language):string => t[lang] ? t[lang][key] || "-" : t["en"][key];
|
||||
/**
|
||||
* Emoji dialog with list of emoji buttons
|
||||
*/
|
||||
class WCWYSIWYGExtensionEmojiDialog {
|
||||
Dialog: HTMLDialogElement|null = null
|
||||
WCWYSIWYG:any
|
||||
constructor(WCWYSIWYG:any) {
|
||||
this.WCWYSIWYG = WCWYSIWYG;
|
||||
}
|
||||
connectedCallback() {
|
||||
this.WCWYSIWYG.EditorActionsSection.append(el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
props: {
|
||||
innerHTML: '😃',
|
||||
onpointerup: () => this.#showDialog(),
|
||||
},
|
||||
attrs: {
|
||||
"data-hint": _t('emoji')
|
||||
}
|
||||
}));
|
||||
}
|
||||
#showDialog() {
|
||||
//Check if first show dialog
|
||||
if(this.Dialog === null) {
|
||||
const emojiRanges = {
|
||||
emoticons: [128513, 128591],
|
||||
dingbats: [9986,10160],
|
||||
map: [128640,128704],
|
||||
additional: [127757,128359],
|
||||
};
|
||||
this.Dialog = el('dialog', {
|
||||
classList: ['wc-wysiwyg_dialog', '-modal'],
|
||||
styles: {
|
||||
maxWidth: '90vw',
|
||||
},
|
||||
props: {
|
||||
onpointerup: event => {
|
||||
const target = event.target;
|
||||
if(target.tagName === 'BUTTON') {
|
||||
const emojiCode = target.innerHTML;
|
||||
const data = [
|
||||
new ClipboardItem({ "text/html": new Blob([emojiCode], { type:"text/html" }) }),
|
||||
];
|
||||
navigator.clipboard.write(data).then(
|
||||
() => {
|
||||
console.log('writed');
|
||||
this.Dialog.close();
|
||||
},
|
||||
(err) => { throw new Error("WC-WYSIWYG: Copy emoji to clipboard failed", err); }
|
||||
);
|
||||
}
|
||||
},
|
||||
innerHTML: (() => {
|
||||
let html = '';
|
||||
let showFirst = false;
|
||||
for (let range in emojiRanges) {
|
||||
html += `<details class="wc-wysiwyg_ed" ${showFirst === false ? 'open': ''}><summary>${_t(range)}</summary>`;
|
||||
for (let emojiCode = emojiRanges[range][0]; emojiCode < emojiRanges[range][1]; emojiCode++) {
|
||||
html += '<button class="wc-wysiwyg_btn -emoji" data-hint="' + _t('action') + '">&#'+emojiCode+';</button>';
|
||||
}
|
||||
html += '</details>';
|
||||
showFirst = true;
|
||||
}
|
||||
return html;
|
||||
})()
|
||||
}
|
||||
});
|
||||
this.WCWYSIWYG.append(this.Dialog);
|
||||
}
|
||||
this.Dialog.showModal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Put extension in global view
|
||||
(window._WCWYSIWYG !== undefined) ? window._WCWYSIWYG.extensions.push(WCWYSIWYGExtensionEmojiDialog) : window._WCWYSIWYG = {extensions:[WCWYSIWYGExtensionEmojiDialog]};
|
||||
|
||||
export default WCWYSIWYGExtensionEmojiDialog;
|
114
src/extensions/presetList.ts
Normal file
114
src/extensions/presetList.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { el } from '../core/el.js';
|
||||
|
||||
/**
|
||||
* Presets format
|
||||
*/
|
||||
interface WCWYSIWYGPreset {
|
||||
/**
|
||||
* Preset name on drop down
|
||||
*/
|
||||
name:string,
|
||||
/**
|
||||
* tag name
|
||||
*/
|
||||
tag:string,
|
||||
/**
|
||||
* will set to HTMLElement.className property
|
||||
*/
|
||||
class?:string,
|
||||
/**
|
||||
* will set to HTMLElement.style property
|
||||
*/
|
||||
style?:string
|
||||
}
|
||||
class WCWYSIWYGExtensionPresetList {
|
||||
PresetList: HTMLElement|null = null
|
||||
PresetBtn:HTMLElement
|
||||
Presets: Array<WCWYSIWYGPreset> = []
|
||||
WCWYSIWYG:any
|
||||
constructor(WCWYSIWYG:any) {
|
||||
this.WCWYSIWYG = WCWYSIWYG;
|
||||
this.Presets = JSON.parse(WCWYSIWYG.getAttribute('data-preset-list'));
|
||||
this.PresetBtn = el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
props: {
|
||||
innerHTML: 'Оформление',
|
||||
onpointerup: () => this.#showPresetList(),
|
||||
},
|
||||
});
|
||||
}
|
||||
connectedCallback() {
|
||||
if(this.Presets !== null) {
|
||||
this.WCWYSIWYG.EditorActionsSection.append(this.PresetBtn);
|
||||
}
|
||||
}
|
||||
#showPresetList() {
|
||||
console.log('show dialog');
|
||||
if(this.PresetList === null) {
|
||||
const section = el("section", { });
|
||||
//Make presets blocks
|
||||
this.Presets.forEach(preset => {
|
||||
const presetEl = el(preset.tag, {
|
||||
props: {
|
||||
innerHTML: `${preset.name}`,
|
||||
className: preset.class || null,
|
||||
style: preset.style || null,
|
||||
}
|
||||
});
|
||||
section.append(el('button', {
|
||||
props: {
|
||||
type: 'button',
|
||||
style: "cursor:pointer;border: 1px solid rgba(0,0,0,0.5); margin-bottom:5px; border-radius:5px; background: transparent; display:block;",
|
||||
onclick: e => this.#makePreset(preset),
|
||||
},
|
||||
append: [presetEl],
|
||||
}));
|
||||
});
|
||||
|
||||
//Close dialog button
|
||||
this.PresetList = el('div', {
|
||||
// classList: ['wc-wysiwyg_ec'],
|
||||
append: [
|
||||
section,
|
||||
el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
props: {
|
||||
type: 'button',
|
||||
innerText: 'Закрыть',
|
||||
onclick: event => this.PresetList.style.display = 'none'
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
// this.WCWYSIWYG.EditorActionsSection.append(this.PresetList);
|
||||
this.WCWYSIWYG.EditorActionsSection.insertAdjacentElement('beforeend', this.PresetList);
|
||||
// this.PresetBtn.append(this.PresetList);
|
||||
}
|
||||
// this.PresetList.show();
|
||||
this.PresetList.style.display = 'block';
|
||||
}
|
||||
#makePreset(preset:WCWYSIWYGPreset) {
|
||||
const editorSelection = this.WCWYSIWYG.getSelection();
|
||||
|
||||
console.log('make preset', preset, editorSelection);
|
||||
let tagNode = el(preset.tag, {});
|
||||
if(preset.style) {
|
||||
tagNode.setAttribute('style', preset.style);
|
||||
}
|
||||
if(preset.class) {
|
||||
tagNode.className = preset.class;
|
||||
}
|
||||
if (editorSelection.selection !== null && editorSelection.selection.rangeCount && editorSelection.text !== null) {
|
||||
const range = editorSelection.selection.getRangeAt(0).cloneRange();
|
||||
range.surroundContents(tagNode);
|
||||
editorSelection.selection.removeAllRanges();
|
||||
editorSelection.selection.addRange(range);
|
||||
tagNode.innerText = editorSelection.text;
|
||||
this.WCWYSIWYG.updateContent();
|
||||
// this.WCWYSIWY.#checkEditProps({target:tagNode, stopPropagation: () => false});
|
||||
}
|
||||
}
|
||||
}
|
||||
//Put extension in global view
|
||||
(window._WCWYSIWYG !== undefined) ? window._WCWYSIWYG.extensions.push(WCWYSIWYGExtensionPresetList) : window._WCWYSIWYG = {extensions:[WCWYSIWYGExtensionPresetList]};
|
||||
export default WCWYSIWYGExtensionPresetList;
|
@ -1,11 +1,142 @@
|
||||
:root {
|
||||
// orange
|
||||
--color-orange-50: #FFF8E1;
|
||||
--color-orange-100: #FFF8E1;
|
||||
--color-orange-500: #FF9800;
|
||||
--color-orange-700: #F57C00;
|
||||
|
||||
--color-green-50: #E8F5E9;
|
||||
--color-green-100: #C8E6C9;
|
||||
--color-green-300: #AED581;
|
||||
--color-green-500: #4CAF50;
|
||||
--color-green-900: #1B5E20;
|
||||
|
||||
--color-red-50: #FFEBEE;
|
||||
--color-red-100: #FFCDD2;
|
||||
--color-red-200: #FF8A65;
|
||||
--color-red-500: #F44336;
|
||||
--color-red-900: #B71C1C;
|
||||
|
||||
--color-amber-50: #FFF8E1;
|
||||
--color-amber-100: #FFE0B2;
|
||||
--color-amber-900: #FF6F00;
|
||||
|
||||
--color-indigo-100: #C5CAE9;
|
||||
|
||||
// orange
|
||||
--color-deep-orange-50: #FBE9E7;
|
||||
--color-deep-orange-900: #BF360C;
|
||||
|
||||
// lime
|
||||
--color-lime-50: #F9FBE7;
|
||||
|
||||
// grey
|
||||
--color-grey-50: #FAFAFA;
|
||||
--color-grey-100: #F5F5F5;
|
||||
--color-grey-200: #EEEEEE;
|
||||
--color-grey-300: #E0E0E0;
|
||||
--color-grey-400: #BDBDBD;
|
||||
--color-grey-500: #9E9E9E;
|
||||
--color-grey-600: #757575;
|
||||
--color-grey-700: #757575;
|
||||
--color-grey-800: #424242;
|
||||
--color-grey-900: #212121;
|
||||
|
||||
// blue
|
||||
--color-blue-50: #E3F2FD;
|
||||
--color-blue-100: #BBDEFB;
|
||||
--color-blue-200: #90CAF9;
|
||||
--color-blue-300: #64B5F6;
|
||||
--color-blue-400: #42A5F5;
|
||||
--color-blue-500: #2196F3;
|
||||
--color-blue-800: #1565C0;
|
||||
--color-blue-900: #0D47A1;
|
||||
|
||||
// blue-gray
|
||||
--color-blue-gray-50: #ECEFF1;
|
||||
--color-blue-gray-100: #CFD8DC;
|
||||
--color-blue-gray-200: #B0BEC5;
|
||||
--color-blue-gray-300: #90A4AE;
|
||||
--color-blue-gray-400: #78909C;
|
||||
--color-blue-gray-700: #455A64;
|
||||
--color-blue-gray-800: #37474F;
|
||||
--color-blue-gray-900: #263238;
|
||||
// blue-light
|
||||
--color-blue-light-50: #E1F5FE;
|
||||
--color-blue-light-100: #B3E5FC;
|
||||
|
||||
--wc-wysiwyg-light: #fff;
|
||||
--wc-wysiwyg-dark: #37474F;
|
||||
}
|
||||
wc-wysiwyg.-word .wc-wysiwyg_pf > label {
|
||||
background-color: var(--color-blue-500);
|
||||
}
|
||||
wc-wysiwyg.-word .wc-wysiwyg_pf {
|
||||
background-color: var(--color-blue-400);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
wc-wysiwyg.-word .wc-wysiwyg_content {
|
||||
border-radius: 5px;
|
||||
}
|
||||
wc-wysiwyg.-word .wc-wysiwyg_bt {
|
||||
background-color: var(--color-blue-300);
|
||||
padding:5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
wc-wysiwyg.-word .wc-wysiwyg_ec {
|
||||
background-color: var(--wc-wysiwyg-light);
|
||||
padding:5px;
|
||||
border-radius: 3px;
|
||||
margin: 5px 0;
|
||||
border: 0;
|
||||
}
|
||||
.wc-wysiwyg {
|
||||
background-color: #eee;
|
||||
background-color: var(--wc-wysiwyg-light);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
position: relative;
|
||||
border: 1px solid var(--color-blue-gray-400);
|
||||
padding:5px;
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
&_dialog {
|
||||
border-radius: 10px;
|
||||
border:none;
|
||||
outline: none;
|
||||
&::backdrop {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
&.-modal {
|
||||
min-width: 90vw;
|
||||
min-height: 90vh;
|
||||
max-height: 90vh;
|
||||
max-height: 90vh;
|
||||
}
|
||||
&.-colors {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
max-width: 300px;
|
||||
&:not([open]) {
|
||||
display: none;
|
||||
}
|
||||
& fieldset.-palette {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
max-width: 20px;
|
||||
& > button {
|
||||
flex: 1;
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&_bt {
|
||||
display: block;
|
||||
padding:5px;
|
||||
@ -27,31 +158,23 @@
|
||||
|
||||
&_ce {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 7px;
|
||||
border: 1px solid var(--color-blue-500);
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-blue-50);
|
||||
}
|
||||
&_ce:before {
|
||||
content: 'HTML5 custom-elements';
|
||||
color: #fff;
|
||||
background-color: var(--color-blue-500);
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
line-height: 0.6em;
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
transform: translate(0, -50%);
|
||||
top:0;
|
||||
left:0;
|
||||
}
|
||||
&_ce > button {
|
||||
border-color: var(--color-blue-500);
|
||||
background-color: var(--color-blue-100);
|
||||
&:hover {
|
||||
border-color: var(--color-blue-900);
|
||||
background-color: var(--color-blue-200);
|
||||
margin-bottom: 5px;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
background: rgba(0,0,0,0.2);
|
||||
&:before {
|
||||
content: 'HTML5 custom-elements';
|
||||
color: #fff;
|
||||
background-color: var(--color-blue-500);
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
line-height: 0.6em;
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
transform: translate(0, -50%);
|
||||
top:0;
|
||||
left:0;
|
||||
}
|
||||
}
|
||||
/* preview */
|
||||
@ -62,7 +185,7 @@
|
||||
min-height: 200px;
|
||||
}
|
||||
&_content {
|
||||
padding:5px 5px 2em 5px;
|
||||
padding:0.5em;
|
||||
border:1px solid #ccc;
|
||||
background: #fff;
|
||||
overflow-x: hidden;
|
||||
@ -72,7 +195,7 @@
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
display: block;
|
||||
resize: vertical;
|
||||
& .-selected {
|
||||
background-color: var(--color-blue-100);
|
||||
@ -86,52 +209,237 @@
|
||||
}
|
||||
}
|
||||
&_ec {
|
||||
background: var(--color-blue-gray-100);
|
||||
padding: 0.5em 0.25em 0.25em 0.25em;
|
||||
border-radius: 3px;
|
||||
border: 1px solid;
|
||||
border-color: var(--color-blue-gray-100);
|
||||
}
|
||||
&_ec:focus-within {
|
||||
border-color: var(--color-blue-500);
|
||||
background: #2b393f;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
top:0;
|
||||
position: sticky;
|
||||
z-index: 2;
|
||||
}
|
||||
&_btn {
|
||||
background: var(--color-blue-gray-50);
|
||||
outline: none;
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--color-blue-gray-200);
|
||||
border-bottom: 3px solid var(--color-blue-gray-200);
|
||||
color: #333;
|
||||
min-width: 0px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.85em;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
margin-right: 5px;
|
||||
min-width: 30px;
|
||||
line-height: 20px;
|
||||
background-color: var(--wc-wysiwyg-light);
|
||||
box-shadow: 1px 2px 5px rgba(0,0,0,0.3);
|
||||
border:0;
|
||||
border-radius: 5px;
|
||||
padding:2px 5px;
|
||||
margin-right:5px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: var(--color-blue-gray-100);
|
||||
border-color: var(--color-blue-gray-300);
|
||||
box-shadow: 0 2px 5px rgba(0, 110, 253, 0.9);
|
||||
}
|
||||
&:focus {
|
||||
border-color: var(--color-blue-500);
|
||||
box-shadow: 0 2px 5px rgba(0, 110, 253, 0.9);
|
||||
}
|
||||
&:active {
|
||||
padding-top:2px;
|
||||
background: var(--color-blue-gray-100);
|
||||
border-color: var(--color-blue-gray-700);
|
||||
border-bottom: 1px solid;
|
||||
box-shadow: 0 2px 5px rgba(1, 181, 52, 0.9);
|
||||
}
|
||||
&.-clear {
|
||||
text-decoration: line-through;
|
||||
font-weight: bold;
|
||||
}
|
||||
&.-emoji {
|
||||
border:none;
|
||||
font-size: 20px;
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
line-height: 32px;
|
||||
box-sizing: border-box;
|
||||
padding:0;
|
||||
border-radius: 1em;
|
||||
margin: 2px;
|
||||
}
|
||||
&.-color {
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
border-color: rgba(0,0,0,0.2);
|
||||
}
|
||||
&.-prevcolor {
|
||||
// background-color: transparent;
|
||||
// border: 1px solid #ccc;
|
||||
&::before {
|
||||
display: inline-block;
|
||||
background-color: var(--colorer);
|
||||
width: 12px;
|
||||
content: '';
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(0,0,0,0.5);
|
||||
}
|
||||
}
|
||||
&.-b {
|
||||
font-weight: bold;
|
||||
}
|
||||
&.-i {
|
||||
font-style: italic;
|
||||
}
|
||||
&.-u {
|
||||
text-decoration: underline;
|
||||
}
|
||||
&.-s {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
&.-sub {
|
||||
vertical-align: sub;
|
||||
font-size: 0.5em;
|
||||
}
|
||||
&.-del::before {
|
||||
content: '- ';
|
||||
font-weight: 400;
|
||||
}
|
||||
&.-h1::before {
|
||||
content: '§ ';
|
||||
font-weight: 400;
|
||||
}
|
||||
&.-del {
|
||||
color: var(--color-red-900);
|
||||
border-bottom: 1px solid var(--color-red-900);
|
||||
background-color: var(--color-red-50);
|
||||
}
|
||||
&.-a::before {
|
||||
content: "🔗 ";
|
||||
}
|
||||
&.-ul::before {
|
||||
content: "● ";
|
||||
}
|
||||
&.-ol::before {
|
||||
content: "1. ";
|
||||
}
|
||||
&.-var::before {
|
||||
content: "∫ ";
|
||||
}
|
||||
&.-var {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
&.-details:before {
|
||||
content: "→";
|
||||
}
|
||||
&.-details {
|
||||
text-decoration: dotted;
|
||||
border: 1px dashed var(--color-blue-gray-200);
|
||||
}
|
||||
&.-pre::before {
|
||||
content: "...";
|
||||
display: inline-block;
|
||||
background-color: var(--color-blue-gray-100);
|
||||
color: var(--color-blue-grey-900);
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0 3px;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.8em;
|
||||
line-height: 10px;
|
||||
}
|
||||
&.-pre {
|
||||
background-color: var(--color-blue-gray-50);
|
||||
color: var(--color-blue-grey-900);
|
||||
padding-left: 15px;
|
||||
}
|
||||
&.-ins::before {
|
||||
content: '+ ';
|
||||
font-weight: 400;
|
||||
}
|
||||
&.-ins {
|
||||
color: var(--color-green-900);
|
||||
border-bottom: 1px solid var(--color-green-900);
|
||||
background-color: var(--color-green-50);
|
||||
}
|
||||
&.-sup {
|
||||
vertical-align: super;
|
||||
font-size: 0.5em;
|
||||
}
|
||||
&.-q:before {
|
||||
content: open-quote;
|
||||
}
|
||||
&.-samp:before {
|
||||
content: '> ';
|
||||
color: var(--color-blue-gray-300);
|
||||
font-family: sans-serif;
|
||||
}
|
||||
&.-samp {
|
||||
background-color: var(--color-blue-gray-50);
|
||||
border-bottom: 1px solid var(--color-blue-gray-300);
|
||||
}
|
||||
&.-blockquote::before {
|
||||
content: '❝';
|
||||
font-size: 1em;
|
||||
color: #F57F17;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 4px;
|
||||
user-select: none;
|
||||
}
|
||||
&.-blockquote {
|
||||
background-color: var(--color-amber-50);
|
||||
color: #412207;
|
||||
padding-left: 15px;
|
||||
border-left: 2px solid #F57F17;
|
||||
}
|
||||
&.-time:before {
|
||||
content: "📅 ";
|
||||
}
|
||||
&.-img:before {
|
||||
content: "🌅 ";
|
||||
}
|
||||
&.-video:before {
|
||||
content: "🎦 ";
|
||||
}
|
||||
&.-audio:before {
|
||||
content: "🎵 ";
|
||||
}
|
||||
&.-details:before {
|
||||
content: "▸ ";
|
||||
}
|
||||
&.-code {
|
||||
content: "<code>";
|
||||
background-color: var(--color-blue-gray-50);
|
||||
color: var(--color-blue-grey-900);
|
||||
}
|
||||
&.-strong {
|
||||
background-color: var(--color-deep-orange-50);
|
||||
color: var(--color-deep-orange-900);
|
||||
font-weight: 400;
|
||||
}
|
||||
&.-abbr {
|
||||
color: #1A237E;
|
||||
}
|
||||
&.-q {
|
||||
color:#000;
|
||||
background-color: #FFF8E1;
|
||||
}
|
||||
&.-small {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
&.-dfn {
|
||||
color: var(--color-blue-900);
|
||||
font-style: italic;
|
||||
}
|
||||
&.-mark {
|
||||
background-color: var(--color-lime-100);
|
||||
color: var(--color-lime-900);
|
||||
&:hover {
|
||||
background-color: var(--color-lime-200);
|
||||
color: var(--color-lime-900);
|
||||
}
|
||||
}
|
||||
&.-kbd {
|
||||
background: var(--color-blue-gray-50);
|
||||
outline: none;
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--color-blue-gray-200);
|
||||
border-bottom: 3px solid var(--color-blue-gray-200);
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
&_ia {
|
||||
display: flex;
|
||||
@ -148,11 +456,10 @@
|
||||
position: fixed;
|
||||
bottom:0;
|
||||
width: 100%;
|
||||
background: var(--color-blue-gray-50);
|
||||
padding:3px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--color-blue-gray-300);
|
||||
box-sizing: border-box;
|
||||
background-color: var(--wc-wysiwyg-light);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
& > form {
|
||||
&:nth-child(1n+2) {
|
||||
margin-top: 10px;
|
||||
@ -184,7 +491,8 @@
|
||||
background-color: var(--color-blue-200);
|
||||
color: var(--color-blue-gray-800);
|
||||
padding: 3px 3px 3px 5px;
|
||||
display: flex;
|
||||
margin: 5px 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
margin-right: 5px;
|
||||
@ -239,4 +547,23 @@
|
||||
& .-display-none {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wc-wysiwyg {
|
||||
background-color: var(--wc-wysiwyg-dark);
|
||||
&_di {
|
||||
background-color: var(--wc-wysiwyg-dark);
|
||||
}
|
||||
&_bt {
|
||||
background-color: var(--wc-wysiwyg-dark);
|
||||
}
|
||||
&_btn {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
color: #ccc;
|
||||
&:hover {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,14 @@
|
||||
import {t} from './core/translates.js';
|
||||
import { t } from './core/translates.js';
|
||||
import { el } from "./core/el.js";
|
||||
|
||||
interface WCWYSIWYGTag {
|
||||
tag:string
|
||||
method?:Function,
|
||||
hint?:string,
|
||||
is?: string,
|
||||
}
|
||||
interface WCWYSIWYGActions {
|
||||
wrapTag: Function,
|
||||
insertImageBlock: Function,
|
||||
insertAudio: Function,
|
||||
insertVideo: Function,
|
||||
}
|
||||
import test from './extensions/colorerDialog.js';
|
||||
import test2 from './extensions/presetList.js';
|
||||
/**
|
||||
* Translate function
|
||||
* @param key:string phrase key
|
||||
* @param lang:string language key defaul navigatol.language
|
||||
* @returns
|
||||
*/
|
||||
const _t = (key:string, lang = navigator.language):string => t[lang] ? t[lang][key] || "-" : t["en"][key];
|
||||
|
||||
//All semantic html5 known editor tags
|
||||
const allTags = [
|
||||
@ -52,13 +48,30 @@ const allTags = [
|
||||
{ tag: 'blockquote'},
|
||||
{ tag: 'details'},
|
||||
] as WCWYSIWYGTag[];
|
||||
|
||||
/**
|
||||
* Base class for WCWYSIWYG web component
|
||||
*/
|
||||
class WCWYSIWYG extends HTMLElement {
|
||||
/**
|
||||
* Tags included to action bar in editor
|
||||
*/
|
||||
public EditorTags:WCWYSIWYGTag[]
|
||||
/**
|
||||
* Custom tags included to action bar in editor
|
||||
*/
|
||||
public EditorCustomTags:WCWYSIWYGTag[]
|
||||
//Content editable wc-editor element
|
||||
/**
|
||||
* Main ContentEditable element
|
||||
*/
|
||||
public EditorNode:HTMLElement
|
||||
/**
|
||||
* Editor actions block elements with EditorTags buttons
|
||||
*/
|
||||
public EditorActionsSection:HTMLElement
|
||||
/**
|
||||
* Custom web-components buttons <fieldset>
|
||||
*/
|
||||
public EditorCustomTagsForm?:HTMLElement
|
||||
//Inline edites
|
||||
public EditorInlineActions:any[]
|
||||
public EditorInlineDialog:HTMLDialogElement
|
||||
@ -66,7 +79,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
//Editor props
|
||||
public EditorPropertyForm?:HTMLElement
|
||||
//Clear btn
|
||||
public EditorClearFormatBtn:HTMLElement
|
||||
public EditorClearFormatBtn:HTMLButtonElement
|
||||
//Autocomplete area
|
||||
public EditorAutoCompleteForm?:HTMLElement
|
||||
//Bottom actions
|
||||
@ -75,7 +88,6 @@ class WCWYSIWYG extends HTMLElement {
|
||||
public EditorBottomFormViewToggle?:HTMLElement
|
||||
|
||||
public EditorPreviewText:HTMLTextAreaElement
|
||||
public EditorCustomTagsForm?:HTMLElement
|
||||
public EditorTagsMethods:WCWYSIWYGActions
|
||||
public EditorAllowTags:string[]
|
||||
public EditorFullScreenButton?:HTMLElement
|
||||
@ -86,19 +98,21 @@ class WCWYSIWYG extends HTMLElement {
|
||||
|
||||
#EditProps:boolean|object
|
||||
#Autocomplete:boolean
|
||||
#Extensions?: any[]
|
||||
#SotrageKey:string|null
|
||||
#HideBottomActions:boolean
|
||||
#Connected:boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#checkExtensions();
|
||||
this.classList.add('wc-wysiwyg');
|
||||
|
||||
//Listen root element events
|
||||
this.onpointerup = (event) => {
|
||||
const selection = window.getSelection();
|
||||
const editorSelection = this.getSelection();
|
||||
//if check exist selection string
|
||||
if(selection !== null && selection.toString().length > 0) {
|
||||
if(editorSelection.selection !== null && editorSelection.selection.text !== null) {
|
||||
this.EditorInlineActionsForm.style.display = '';
|
||||
if(this.EditorPropertyForm){
|
||||
this.EditorPropertyForm.style.display = 'none';
|
||||
@ -116,6 +130,25 @@ class WCWYSIWYG extends HTMLElement {
|
||||
|
||||
connectedCallback() {
|
||||
if(this.#Connected === false) {
|
||||
const asyncExtensions = this.getAttribute('data-async-extensions') || false;
|
||||
if(asyncExtensions !== false) {
|
||||
const asyncExtensionsPaths = asyncExtensions.split(',');
|
||||
console.log('need load', asyncExtensionsPaths);
|
||||
asyncExtensionsPaths.forEach((extensionPath:string) => {
|
||||
import(extensionPath).then(esm => {
|
||||
console.log('asyncExtensionsPaths loaded', esm);
|
||||
const Extension = new esm.default(this);
|
||||
if(typeof Extension.connectedCallback === 'function') {
|
||||
Extension.connectedCallback();
|
||||
}
|
||||
if(this.#Extensions instanceof Array) {
|
||||
this.#Extensions.push(Extension);
|
||||
} else {
|
||||
this.#Extensions = [Extension];
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
//Check Tags
|
||||
const allowTags = this.getAttribute('data-allow-tags') || allTags.map(t => t.tag).join(',');
|
||||
|
||||
@ -138,30 +171,39 @@ class WCWYSIWYG extends HTMLElement {
|
||||
//allow inline without ['video','audio','img']
|
||||
this.EditorInlineActions = this.EditorTags.filter(action => ['video','audio','img'].includes(action.tag) === false);
|
||||
|
||||
this.EditorActionsSection = el('section', { classList: ['wc-wysiwyg_ec'] });
|
||||
//Clear format button
|
||||
this.EditorClearFormatBtn = el('button', {
|
||||
classList: ['wc-wysiwyg_btn', '-clear'],
|
||||
attrs: {
|
||||
'data-hint': this.#t('clearFormat'),
|
||||
'data-hint': _t('clearFormat'),
|
||||
},
|
||||
props: {
|
||||
innerHTML:'Ⱦ',
|
||||
},
|
||||
});
|
||||
//Inline selection actions panel
|
||||
this.EditorInlineActionsForm = el('form');
|
||||
this.EditorInlineDialog = el('dialog', {
|
||||
classList: ['wc-wysiwyg_di'],
|
||||
append: [this.EditorInlineActionsForm, this.EditorClearFormatBtn],
|
||||
props: {
|
||||
//prevent submit
|
||||
onsubmit: event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
},
|
||||
}
|
||||
//Top editor actions sections
|
||||
this.EditorActionsSection = el('section', {
|
||||
classList: ['wc-wysiwyg_ec'],
|
||||
});
|
||||
this.#makeActionButtons(this.EditorActionsSection, this.EditorTags);
|
||||
this.EditorActionsSection.append(this.EditorClearFormatBtn);
|
||||
|
||||
//Inline selection actions panel
|
||||
// this.EditorInlineActionsForm = el('form');
|
||||
// this.EditorInlineDialog = el('dialog', {
|
||||
// classList: ['wc-wysiwyg_di'],
|
||||
// append: [this.EditorInlineActionsForm],
|
||||
// props: {
|
||||
// //prevent submit
|
||||
// onsubmit: event => {
|
||||
// event.preventDefault();
|
||||
// event.stopPropagation();
|
||||
// },
|
||||
// },
|
||||
// attrs: {
|
||||
// tabIndex: -1,
|
||||
// }
|
||||
// });
|
||||
|
||||
//Edit props
|
||||
if(this.#EditProps) {
|
||||
@ -172,12 +214,12 @@ class WCWYSIWYG extends HTMLElement {
|
||||
props: {
|
||||
onsubmit: event => {
|
||||
event.preventDefault();
|
||||
this.hideEditorInlineDialog();
|
||||
this.EditorPropertyForm.style.display = 'none';
|
||||
},
|
||||
onpointerup: event => event.stopPropagation(),
|
||||
}
|
||||
});
|
||||
this.EditorInlineDialog.append(this.EditorPropertyForm);
|
||||
this.EditorActionsSection.append(this.EditorPropertyForm);
|
||||
}
|
||||
|
||||
//Autocomplete form
|
||||
@ -210,11 +252,11 @@ class WCWYSIWYG extends HTMLElement {
|
||||
//Custom panel tags
|
||||
this.EditorCustomTagsForm = el('fieldset', {
|
||||
classList: ['wc-wysiwyg_ce'],
|
||||
});
|
||||
}) as HTMLElement;
|
||||
//Make custom actions buttons panel
|
||||
this.#makeActionButtons(this.EditorCustomTagsForm as HTMLElement, this.EditorCustomTags);
|
||||
this.#makeActionButtons(this.EditorCustomTagsForm, this.EditorCustomTags);
|
||||
|
||||
this.appendChild(this.EditorCustomTagsForm as HTMLElement);
|
||||
this.EditorActionsSection.prepend(this.EditorCustomTagsForm);
|
||||
}
|
||||
|
||||
//Node editable
|
||||
@ -238,19 +280,32 @@ class WCWYSIWYG extends HTMLElement {
|
||||
},
|
||||
//Check hot keys is pressed
|
||||
onkeydown: event => {
|
||||
this.#checkKeyBindings(event)
|
||||
this.#checkKeyBindings(event);
|
||||
},
|
||||
onpaste: (event:ClipboardEvent) => {
|
||||
// if(event.type === 'paste') {
|
||||
// window.navigator.clipboard.readText().then(text => {
|
||||
// const target = event.target as HTMLElement;
|
||||
// //Check isnert html symbol
|
||||
// if(text.startsWith('&#') === false) {
|
||||
// if(target.tagName === 'BR') {
|
||||
// target.insertAdjacentText('afterend',text);
|
||||
// }
|
||||
// this.updateContent();
|
||||
// event.preventDefault();
|
||||
// event.stopPropagation();
|
||||
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
//Make action buttons
|
||||
this.#makeActionButtons(this.EditorActionsSection, this.EditorTags);
|
||||
this.#makeActionButtons(this.EditorInlineActionsForm, this.EditorInlineActions);
|
||||
|
||||
//Inser wc-editor after textarea node
|
||||
this.append(
|
||||
this.EditorActionsSection,
|
||||
this.EditorInlineDialog,
|
||||
this.EditorNode,
|
||||
);
|
||||
|
||||
@ -259,7 +314,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
this.EditorBottomFormViewToggle = el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
attrs: {
|
||||
'data-hint': this.#t('toggleViewMode'),
|
||||
'data-hint': _t('toggleViewMode'),
|
||||
'data-mode': 'html5',
|
||||
},
|
||||
props: {
|
||||
@ -281,11 +336,11 @@ class WCWYSIWYG extends HTMLElement {
|
||||
this.EditorBottomFormNewP = el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
attrs: {
|
||||
'data-hint': this.#t('addNewParahraph'),
|
||||
'data-hint': _t('addNewParahraph'),
|
||||
},
|
||||
props: {
|
||||
type:'button',
|
||||
innerText: '+ P',
|
||||
innerText: '+ ❡',
|
||||
onpointerup: event => {
|
||||
const P = el('p', {props: {innerText: '/'}});
|
||||
this.EditorNode.appendChild(P);
|
||||
@ -297,7 +352,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
this.EditorFullScreenButton = el('button', {
|
||||
classList: ['wc-wysiwyg_btn'],
|
||||
attrs: {
|
||||
'data-hint': this.#t('fullScreen'),
|
||||
'data-hint': _t('fullScreen'),
|
||||
},
|
||||
props: {
|
||||
type: "button",
|
||||
@ -319,14 +374,16 @@ class WCWYSIWYG extends HTMLElement {
|
||||
this.EditorNode.innerHTML = this.EditorPreviewText.value;
|
||||
//Check local storage key
|
||||
this.#SotrageKey = this.getAttribute('data-storage');
|
||||
console.log('storage key is ', this.#SotrageKey);
|
||||
if(this.#SotrageKey) {
|
||||
let storeValue = window.localStorage.getItem(this.#SotrageKey);
|
||||
console.log('restore from storage', storeValue);
|
||||
if(storeValue) {
|
||||
this.EditorNode.innerHTML = storeValue;
|
||||
}
|
||||
}
|
||||
//Check and call connectedCallback in extensions
|
||||
if(this.#Extensions instanceof Array) {
|
||||
this.#Extensions.forEach(extension => typeof extension.connectedCallback === 'function' ? extension.connectedCallback(this) : false);
|
||||
}
|
||||
this.updateContent();
|
||||
|
||||
this.#Connected = true;
|
||||
@ -337,14 +394,20 @@ class WCWYSIWYG extends HTMLElement {
|
||||
* Update content value and update behaviors
|
||||
*/
|
||||
updateContent() {
|
||||
// Update the value of the editor node to the current innerHTML
|
||||
this.value = this.EditorNode.innerHTML;
|
||||
// Update the value of the EditorPreviewText to the current value
|
||||
this.EditorPreviewText.value = this.value;
|
||||
// Check the validity of the current value
|
||||
this.checkValidity();
|
||||
// Dispatch an event to indicate that the input has been changed
|
||||
this.dispatchEvent(new Event('oninput', { bubbles: true, cancelable: false }));
|
||||
// Update the preview element with the data attribute 'data-preview-el'
|
||||
this.updatePreviewEl(this.getAttribute('data-preview-el'));
|
||||
//Update storage value if StorageKey exits
|
||||
if(this.#SotrageKey) {
|
||||
window.localStorage.setItem(this.#SotrageKey, this.value);
|
||||
}
|
||||
this.dispatchEvent(new Event('oninput', { bubbles: true, cancelable: false }));
|
||||
this.updatePreviewEl(this.getAttribute('data-preview-el'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -372,19 +435,19 @@ class WCWYSIWYG extends HTMLElement {
|
||||
if(this.getAttribute('required') !== null) {
|
||||
if(String(this.EditorNode.textContent).length === 0) {
|
||||
hasErros = true;
|
||||
errors.push(this.#t('required'));
|
||||
errors.push(_t('required'));
|
||||
}
|
||||
}
|
||||
if(Number(this.getAttribute('minlength'))) {
|
||||
if(String(this.EditorNode.textContent).length < Number(this.getAttribute('minlength'))) {
|
||||
hasErros = true;
|
||||
errors.push(`${this.#t('minlength')} ${this.getAttribute('minlength')}`);
|
||||
errors.push(`${_t('minlength')} ${this.getAttribute('minlength')}`);
|
||||
}
|
||||
}
|
||||
if(Number(this.getAttribute('maxlength'))) {
|
||||
if(String(this.EditorNode.textContent).length > Number(this.getAttribute('maxlength'))) {
|
||||
hasErros = true;
|
||||
errors.push(`${this.#t('maxlength')} ${this.getAttribute('maxlength')}`);
|
||||
errors.push(`${_t('maxlength')} ${this.getAttribute('maxlength')}`);
|
||||
}
|
||||
}
|
||||
const filterTags = this.getAttribute('filtertags');
|
||||
@ -394,7 +457,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
const checkTag = disallowTags[i];
|
||||
if(this.EditorNode.querySelector(checkTag)) {
|
||||
hasErros = true;
|
||||
errors.push(`${this.#t('filtertags')} ${checkTag}`);
|
||||
errors.push(`${_t('filtertags')} ${checkTag}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -422,17 +485,17 @@ class WCWYSIWYG extends HTMLElement {
|
||||
*/
|
||||
#checkAutoComplete() {
|
||||
//CHeck autococmplete
|
||||
const Selecton = window.getSelection();
|
||||
if(Selecton !== null && Selecton.anchorNode !== null) {
|
||||
const SelectionParentEl = Selecton.anchorNode.parentElement as HTMLParagraphElement;
|
||||
const editorSelection = this.getSelection();
|
||||
if(editorSelection.selection !== null && editorSelection.selection.anchorNode !== null) {
|
||||
const SelectionParentEl = editorSelection.selection.anchorNode.parentElement as HTMLParagraphElement;
|
||||
const AutoCompleteForm = this.EditorAutoCompleteForm as HTMLElement;
|
||||
if(SelectionParentEl !== null &&
|
||||
//if empty selection
|
||||
Selecton.toString() === '' &&
|
||||
//and parent node is <p>
|
||||
SelectionParentEl.nodeName === 'P' &&
|
||||
//and parent <p> is parentElement in EditorNode
|
||||
SelectionParentEl.parentElement === this.EditorNode) {
|
||||
//if empty selection
|
||||
editorSelection.text === null &&
|
||||
//and parent node is <p>
|
||||
SelectionParentEl.nodeName === 'P' &&
|
||||
//and parent <p> is parentElement in EditorNode
|
||||
SelectionParentEl.parentElement === this.EditorNode) {
|
||||
//and parent <p> inner text starts with `/`
|
||||
if(SelectionParentEl.innerText.startsWith('/')) {
|
||||
const parsedTagName = SelectionParentEl.innerText.replace('/', '');
|
||||
@ -443,7 +506,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
AutoCompleteForm?.appendChild(el('button', {
|
||||
classList: ['wc-wysiwyg_btn', `-${action.tag}`],
|
||||
attrs: {
|
||||
'data-hint': this.#t(action.tag) || null,
|
||||
'data-hint': _t(action.tag) || null,
|
||||
},
|
||||
props: {
|
||||
type: 'submit',
|
||||
@ -462,7 +525,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -470,7 +533,17 @@ class WCWYSIWYG extends HTMLElement {
|
||||
* Show and position inline actions dialog at targetNode
|
||||
**/
|
||||
showEditorInlineDialog() {
|
||||
this.EditorInlineDialog.show();
|
||||
//Save selection range
|
||||
const editorSelection = this.getSelection();
|
||||
if(editorSelection.selection !== null) {
|
||||
const range = editorSelection.selection.getRangeAt(0).cloneRange();
|
||||
this.EditorInlineDialog.show();
|
||||
//Restore selection range after dialog show
|
||||
editorSelection.selection.removeAllRanges();
|
||||
editorSelection.selection.addRange(range);
|
||||
} else {
|
||||
this.EditorInlineDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -488,19 +561,20 @@ class WCWYSIWYG extends HTMLElement {
|
||||
* @param event
|
||||
*/
|
||||
#checkCanClearElement(event:Event) {
|
||||
const eventTarget = event.target as HTMLElement;
|
||||
const eventTarget = event.target as HTMLElement,
|
||||
clearBtn = this.EditorClearFormatBtn;
|
||||
if(eventTarget !== this.EditorNode) {
|
||||
if(eventTarget.nodeName !== 'P'
|
||||
&& eventTarget.nodeName !== 'SPAN') {
|
||||
this.EditorClearFormatBtn.style.display = 'inline-block';
|
||||
this.EditorClearFormatBtn.innerHTML = `Ⱦ ${eventTarget.nodeName}`,
|
||||
this.EditorClearFormatBtn.onpointerup = (event) => {
|
||||
clearBtn.style.display = 'inline-block';
|
||||
clearBtn.innerHTML = `Ⱦ ${eventTarget.nodeName}`,
|
||||
clearBtn.onpointerup = (event) => {
|
||||
eventTarget.replaceWith(document.createTextNode(eventTarget.textContent));
|
||||
}
|
||||
};
|
||||
this.showEditorInlineDialog();
|
||||
} else {
|
||||
this.EditorClearFormatBtn.style.display = 'none';
|
||||
this.EditorClearFormatBtn.onpointerup = null;
|
||||
clearBtn.style.display = 'none';
|
||||
clearBtn.onpointerup = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -515,8 +589,11 @@ class WCWYSIWYG extends HTMLElement {
|
||||
if(this.#EditProps[eventTarget.nodeName]) {
|
||||
const props = this.#EditProps[eventTarget.nodeName];
|
||||
event.stopPropagation();
|
||||
this.dispatchEvent(new CustomEvent('editprops', {
|
||||
detail: { eventTarget }
|
||||
}));
|
||||
this.EditorPropertyForm.style.display = '';
|
||||
this.showEditorInlineDialog();
|
||||
this.EditorPropertyForm.style.display = 'block';
|
||||
this.EditorPropertyForm.setAttribute('data-tag', eventTarget.nodeName);
|
||||
this.EditorPropertyForm.innerHTML = '';
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
@ -567,18 +644,21 @@ class WCWYSIWYG extends HTMLElement {
|
||||
if(event.altKey) {
|
||||
//alt+space - move caret to parent node next sibling
|
||||
if(event.code === 'Space') {
|
||||
const Selection = window.getSelection();
|
||||
if(Selection?.type === 'Caret') {
|
||||
//insertAdjacentElement dont support textNodes, first insert span
|
||||
const span = el('span');
|
||||
Selection?.anchorNode?.parentElement?.insertAdjacentElement('afterend', span)
|
||||
//after replace span with textnode and select it
|
||||
const textN = document.createTextNode(' ');
|
||||
span.replaceWith(textN);
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(textN);
|
||||
Selection.removeAllRanges();
|
||||
Selection.addRange(range);
|
||||
const editorSelection = this.getSelection();
|
||||
const Selection = editorSelection.selection;
|
||||
if(Selection !== null) {
|
||||
if(Selection.type === 'Caret') {
|
||||
//insertAdjacentElement dont support textNodes, first insert span
|
||||
const span = el('span');
|
||||
Selection.anchorNode?.parentElement?.insertAdjacentElement('afterend', span)
|
||||
//after replace span with textnode and select it
|
||||
const textN = document.createTextNode(' ');
|
||||
span.replaceWith(textN);
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(textN);
|
||||
Selection.removeAllRanges();
|
||||
Selection.addRange(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,7 +668,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
}
|
||||
//enter - set p as default tag in newline
|
||||
if(event.code === 'Enter' && event.shiftKey === false) {
|
||||
const Selection = window.getSelection();
|
||||
const Selection = this.getSelection().selection;
|
||||
let tagName = 'p';
|
||||
//tags with return default browser behavior
|
||||
if(['LI', 'ARTICLE', 'P'].includes(Selection.anchorNode.parentElement.tagName)) {
|
||||
@ -616,7 +696,7 @@ class WCWYSIWYG extends HTMLElement {
|
||||
const button = el('button', {
|
||||
classList: ['wc-wysiwyg_btn', `-${action.tag}`],
|
||||
props: {
|
||||
tabIndex: -1,
|
||||
tabIndex: 0,
|
||||
type:'button',
|
||||
textContent: action.is ? `${action.tag} is=${action.is}` : action.tag,
|
||||
onpointerup: (event) => {
|
||||
@ -625,8 +705,8 @@ class WCWYSIWYG extends HTMLElement {
|
||||
},
|
||||
},
|
||||
attrs: {
|
||||
'data-hint': action.hint ? action.hint : this.#t(action.tag) || '-',
|
||||
}
|
||||
'data-hint': action.hint ? action.hint : _t(action.tag) || '-',
|
||||
},
|
||||
});
|
||||
toEl.appendChild(button);
|
||||
}
|
||||
@ -679,19 +759,19 @@ class WCWYSIWYG extends HTMLElement {
|
||||
|
||||
/**
|
||||
* Wrap content in <tag>
|
||||
* @param tag:WCWYSIWYGTag
|
||||
**/
|
||||
#wrapTag = (tag, is:boolean|string = false) => {
|
||||
const listTag = ['ul', 'ol'].includes(tag) ? tag : false;
|
||||
tag = listTag !== false ? 'li' : tag;
|
||||
const Selection = window.getSelection();
|
||||
let className = null;
|
||||
let defaultOptions = {
|
||||
classList: className ? className : undefined,
|
||||
} as any;
|
||||
#wrapTag = (tag:WCWYSIWYGTag, is:boolean|string = false) => {
|
||||
//First check if this tag is list item
|
||||
const listTag = ['ul', 'ol'].includes(tag.tag) ? tag.tag : false;
|
||||
//If list tag is true - set newTag is list item as <li>
|
||||
const newtag = listTag !== false ? 'li' : tag.tag;
|
||||
const Selection = this.getSelection().selection;
|
||||
let defaultOptions = {} as any;
|
||||
if(is) {
|
||||
defaultOptions.options = {is};
|
||||
}
|
||||
let tagNode = el(tag, defaultOptions);
|
||||
let tagNode = el(newtag, defaultOptions);
|
||||
|
||||
if (Selection !== null && Selection.rangeCount) {
|
||||
if(listTag !== false) {
|
||||
@ -708,6 +788,8 @@ class WCWYSIWYG extends HTMLElement {
|
||||
tagNode.innerText = tag;
|
||||
}
|
||||
this.updateContent();
|
||||
//Check if new tag has editable props
|
||||
this.#checkEditProps({target:tagNode, stopPropagation: () => false});
|
||||
}
|
||||
}
|
||||
|
||||
@ -758,13 +840,46 @@ class WCWYSIWYG extends HTMLElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate function
|
||||
* @param key:string phrase key
|
||||
* @returns
|
||||
* Check available extensions in global scope
|
||||
*/
|
||||
#t(key:string):string {
|
||||
let lang = this.lang;
|
||||
return t[lang] ? t[lang][key] || "-" : t["en"][key];
|
||||
#checkExtensions() {
|
||||
// Check extensions in global
|
||||
const _WCWYSIWYG = window._WCWYSIWYG;
|
||||
console.log('#checkExtensions',_WCWYSIWYG);
|
||||
if(_WCWYSIWYG !== undefined && _WCWYSIWYG.extensions.length > 0) {
|
||||
this.#Extensions = [];
|
||||
for (let i = 0; i < _WCWYSIWYG.extensions.length; i++) {
|
||||
const Extension = new _WCWYSIWYG.extensions[i](this);
|
||||
this.#Extensions.push(Extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get selection info from editor
|
||||
* @returns editorSelection
|
||||
*/
|
||||
getSelection() {
|
||||
const windowSelection = window.getSelection();
|
||||
const editorSelection = {
|
||||
selection: null,
|
||||
element: null,
|
||||
text: null,
|
||||
node: null,
|
||||
};
|
||||
|
||||
if(windowSelection !== null) {
|
||||
editorSelection.selection = windowSelection;
|
||||
editorSelection.node = windowSelection.anchorNode;
|
||||
editorSelection.element = windowSelection.anchorNode.parentElement;
|
||||
let selectionText = windowSelection.toString();
|
||||
if(selectionText.length > 0) {
|
||||
editorSelection.text = selectionText;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('wc selection', editorSelection);
|
||||
|
||||
return editorSelection;
|
||||
}
|
||||
//define WCWYSIWYG as custom element
|
||||
static define(name = 'wc-wysiwyg') {
|
||||
@ -772,4 +887,28 @@ class WCWYSIWYG extends HTMLElement {
|
||||
}
|
||||
}
|
||||
export default WCWYSIWYG;
|
||||
export const define = WCWYSIWYG.define;
|
||||
export const define = WCWYSIWYG.define;
|
||||
export interface WCWYSIWYGTag {
|
||||
// The HTML tag name
|
||||
tag: string,
|
||||
// Optional method to be called when the tag is used
|
||||
method?: Function,
|
||||
// Optional hint to be displayed when the tag button is pressed\hovered
|
||||
hint?: string,
|
||||
// Optional string to specify the custom tag element
|
||||
is?: string,
|
||||
}
|
||||
export interface WCWYSIWYGActions {
|
||||
wrapTag: Function,
|
||||
insertImageBlock: Function,
|
||||
insertAudio: Function,
|
||||
insertVideo: Function,
|
||||
}
|
||||
declare global {
|
||||
interface Window {
|
||||
_WCWYSIWYG: {
|
||||
extensions?:any[],
|
||||
asyncExtensions?:string[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user