mirror of
https://github.com/webislife/wc-wysiwyg.git
synced 2025-07-21 05:47:26 +00:00
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