mirror of
https://github.com/webislife/wc-wysiwyg.git
synced 2025-07-23 19:17:46 +00:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
ef66238fec | |||
071e8f8eee |
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;
|
Reference in New Issue
Block a user