mirror of
https://github.com/codex-team/editor.js
synced 2026-03-16 23:55:49 +01:00
Header plugin (#281)
* header initial * fix styles * eslint fix * add appendCallback * add comments * update styles * add svgs * highlight settings buttons * do not show text plugin in the toolbar * remove svg * Fixing caret behaviour. (#282) Plugins can change their state so that affect on Block's pluginsContent property which is in memory. * remove useless code * fix merge
This commit is contained in:
parent
1853cfa78a
commit
3d03461dc0
11 changed files with 3481 additions and 7259 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -25,38 +25,8 @@
|
|||
<script src="plugins/text/text.js?v=100"></script>
|
||||
<link rel="stylesheet" href="plugins/text/text.css">
|
||||
|
||||
<!--<script src="plugins/header/header.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/header/header.css">-->
|
||||
|
||||
<!--<script src="plugins/code/code.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/code/code.css">-->
|
||||
|
||||
<!--<script src="plugins/link/link.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/link/link.css">-->
|
||||
|
||||
<!--<script src="plugins/quote/quote.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/quote/quote.css">-->
|
||||
|
||||
<!--<script src="plugins/list/list.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/list/list.css">-->
|
||||
|
||||
<!--<script src="plugins/image/image.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/image/image.css">-->
|
||||
|
||||
<!--<script src="plugins/instagram/instagram.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/instagram/instagram.css">-->
|
||||
|
||||
<!--<script src="plugins/twitter/twitter.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/twitter/twitter.css">-->
|
||||
|
||||
<!--<script src="plugins/embed/embed.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/embed/embed.css">-->
|
||||
|
||||
<!--<script src="plugins/raw/raw.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/raw/raw.css">-->
|
||||
|
||||
<!--<script src="plugins/attaches/attaches.js"></script>-->
|
||||
<!--<link rel="stylesheet" href="plugins/attaches/attaches.css">-->
|
||||
<script src="plugins/header/header.js?v=100"></script>
|
||||
<link rel="stylesheet" href="plugins/header/header.css">
|
||||
|
||||
<script src="../build/codex-editor.js?v=108"></script>
|
||||
|
||||
|
|
@ -68,15 +38,13 @@
|
|||
initialBlock : 'text',
|
||||
tools: {
|
||||
text: Text,
|
||||
term: Term
|
||||
header: Header,
|
||||
term: Term,
|
||||
},
|
||||
toolsConfig: {
|
||||
text: {
|
||||
inlineToolbar : true,
|
||||
},
|
||||
quote: {
|
||||
enableLineBreaks : true
|
||||
}
|
||||
},
|
||||
data: {
|
||||
items: [
|
||||
|
|
@ -92,12 +60,40 @@
|
|||
text : 'В <b>JavaScript</b> <a href="https://ifmo.su/ts-classes">нет возможности</a> назначить свойства при объявлении класса — все необходимые значения нужно определять в конструкторе или других методах. При таком подходе объявление свойств неявное, не всегда ясно какие свойства имеет класс. TS решает эту проблему: здесь можно не только объявить свойства класса, но и назначить им начальные значения'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "header",
|
||||
data: {
|
||||
text: "ES6 тебя сожрет",
|
||||
level: 4
|
||||
}
|
||||
},
|
||||
{
|
||||
type : 'text',
|
||||
data : {
|
||||
text : 'Одним из недостатков ES6 классов является невозможность сделать методы и свойства приватными. В TS есть привычные модификаторы: <span class="marked">public</span>, <span class="marked">private</span> и <span class="marked">protected</span>, которые можно использовать как для методов, так и для свойств. По умолчанию, как и в других языках, все свойства имеют модификатор <span class="marked">public</span>.'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "header",
|
||||
data: {
|
||||
text: "Header 2",
|
||||
level: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "header",
|
||||
data: {
|
||||
text: "Header 3",
|
||||
level: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "header",
|
||||
data: {
|
||||
text: "Header 4",
|
||||
level: 4
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
});
|
||||
|
|
@ -106,192 +102,5 @@
|
|||
|
||||
window.editor = editor;
|
||||
|
||||
// var editor2 = new CodexEditor({
|
||||
// holderId : 'cdx',
|
||||
// initialBlock: 'header'
|
||||
// });
|
||||
// codex.editor.start({
|
||||
// holderId : "codex-editor",
|
||||
// initialBlockPlugin : 'text',
|
||||
// // placeholder: 'Прошлой ночью мне приснилось...',
|
||||
// hideToolbar: false,
|
||||
// tools : {
|
||||
// text: {
|
||||
// type: 'text',
|
||||
// iconClassname: 'ce-icon-text',
|
||||
// render: text.render,
|
||||
// validate: text.validate,
|
||||
// save: text.save,
|
||||
// destroy: text.destroy,
|
||||
// allowedToPaste: true,
|
||||
// showInlineToolbar: true,
|
||||
// allowRenderOnPaste: true
|
||||
// },
|
||||
// header: {
|
||||
// type: 'header',
|
||||
// iconClassname: 'ce-icon-header',
|
||||
// appendCallback: header.appendCallback,
|
||||
// makeSettings: header.makeSettings,
|
||||
// render: header.render,
|
||||
// validate: header.validate,
|
||||
// save: header.save,
|
||||
// destroy: header.destroy,
|
||||
// displayInToolbox: true
|
||||
// },
|
||||
// code: {
|
||||
// type: 'code',
|
||||
// iconClassname: 'ce-icon-code',
|
||||
// make: code.make,
|
||||
// appendCallback: null,
|
||||
// settings: null,
|
||||
// render: code.render,
|
||||
// validate: code.validate,
|
||||
// save: code.save,
|
||||
// destroy: code.destroy,
|
||||
// displayInToolbox: true,
|
||||
// enableLineBreaks: true
|
||||
// },
|
||||
// link: {
|
||||
// type: 'link',
|
||||
// iconClassname: 'ce-icon-link',
|
||||
// prepare: link.prepare,
|
||||
// make: link.makeNewBlock,
|
||||
// appendCallback: link.appendCallback,
|
||||
// render: link.render,
|
||||
// validate: link.validate,
|
||||
// save: link.save,
|
||||
// destroy: link.destroy,
|
||||
// displayInToolbox: true,
|
||||
// enableLineBreaks: true
|
||||
// },
|
||||
// list: {
|
||||
// type: 'list',
|
||||
// iconClassname: 'ce-icon-list-bullet',
|
||||
// make: list.make,
|
||||
// appendCallback: null,
|
||||
// makeSettings: list.makeSettings,
|
||||
// render: list.render,
|
||||
// validate: list.validate,
|
||||
// save: list.save,
|
||||
// destroy: list.destroy,
|
||||
// displayInToolbox: true,
|
||||
// showInlineToolbar: true,
|
||||
// enableLineBreaks: true,
|
||||
// allowedToPaste: true,
|
||||
// },
|
||||
// quote: {
|
||||
// type: 'quote',
|
||||
// iconClassname: 'ce-icon-quote',
|
||||
// makeSettings: quote.makeSettings,
|
||||
// prepare: quote.prepare,
|
||||
// render: quote.render,
|
||||
// validate: quote.validate,
|
||||
// save: quote.save,
|
||||
// destroy: quote.destroy,
|
||||
// displayInToolbox: true,
|
||||
// enableLineBreaks: true,
|
||||
// showInlineToolbar: true,
|
||||
// allowedToPaste: true,
|
||||
// config : {
|
||||
// defaultStyle : 'withPhoto'
|
||||
// }
|
||||
// },
|
||||
// image_extended: {
|
||||
// type: 'image_extended',
|
||||
// iconClassname: 'ce-icon-picture',
|
||||
// appendCallback: image.appendCallback,
|
||||
// prepare: image.prepare,
|
||||
// makeSettings: image.makeSettings,
|
||||
// render: image.render,
|
||||
// save: image.save,
|
||||
// destroy: image.destroy,
|
||||
// isStretched: true,
|
||||
// showInlineToolbar: true,
|
||||
// displayInToolbox: true,
|
||||
// renderOnPastePatterns: image.pastePatterns,
|
||||
// config: {
|
||||
// uploadImage : '/writing/uploadImage',
|
||||
// uploadFromUrl : '/club/fetch'
|
||||
// }
|
||||
// },
|
||||
// instagram: {
|
||||
// type: 'instagram',
|
||||
// iconClassname: 'ce-icon-instagram',
|
||||
// prepare: instagram.prepare,
|
||||
// render: instagram.render,
|
||||
// validate: instagram.validate,
|
||||
// save: instagram.save,
|
||||
// destroy: instagram.destroy,
|
||||
// renderOnPastePatterns: instagram.pastePatterns,
|
||||
// },
|
||||
// tweet: {
|
||||
// type: 'tweet',
|
||||
// iconClassname: 'ce-icon-twitter',
|
||||
// prepare: twitter.prepare,
|
||||
// render: twitter.render,
|
||||
// validate: twitter.validate,
|
||||
// save: twitter.save,
|
||||
// destroy: twitter.destroy,
|
||||
// showInlineToolbar : true,
|
||||
// renderOnPastePatterns: twitter.pastePatterns,
|
||||
// config : {
|
||||
// fetchUrl : ''
|
||||
// }
|
||||
// },
|
||||
// video_extended: {
|
||||
// type: 'video_extended',
|
||||
// make: embed.make,
|
||||
// render: embed.render,
|
||||
// save: embed.save,
|
||||
// destroy: embed.destroy,
|
||||
// validate: embed.validate,
|
||||
// renderOnPastePatterns: embed.pastePatterns,
|
||||
// },
|
||||
// raw: {
|
||||
// type: 'raw',
|
||||
// displayInToolbox: true,
|
||||
// iconClassname: 'raw-plugin-icon',
|
||||
// render: rawPlugin.render,
|
||||
// save: rawPlugin.save,
|
||||
// validate: rawPlugin.validate,
|
||||
// destroy: rawPlugin.destroy,
|
||||
// enableLineBreaks: true,
|
||||
// allowPasteHTML: true
|
||||
// },
|
||||
// attaches: {
|
||||
// type: 'attaches',
|
||||
// displayInToolbox: true,
|
||||
// iconClassname: 'cdx-attaches__icon',
|
||||
// prepare: cdxAttaches.prepare,
|
||||
// render: cdxAttaches.render,
|
||||
// save: cdxAttaches.save,
|
||||
// validate: cdxAttaches.validate,
|
||||
// destroy: cdxAttaches.destroy,
|
||||
// appendCallback: cdxAttaches.appendCallback,
|
||||
// config: {
|
||||
// fetchUrl: '/test',
|
||||
// maxSize: 50000,
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
// data : {
|
||||
// id: +new Date(),
|
||||
// items: [
|
||||
// {
|
||||
// type : 'header',
|
||||
// data : {
|
||||
// text : 'Привет от CodeX'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// type : 'text',
|
||||
// data : {
|
||||
// text : 'Пишите нам на team@ifmo.su'
|
||||
// }
|
||||
// },
|
||||
// ],
|
||||
// count: 3
|
||||
// }
|
||||
// });
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,41 +1,24 @@
|
|||
/**
|
||||
* Plugin styles
|
||||
*/
|
||||
* Plugin styles
|
||||
*/
|
||||
.ce-header {
|
||||
padding: .7em 0;
|
||||
margin: 0;
|
||||
line-height: 1.4em;
|
||||
padding: .7em 0;
|
||||
margin: 0;
|
||||
line-height: 1.4em;
|
||||
outline: none;
|
||||
}
|
||||
.ce-header p,
|
||||
.ce-header div{
|
||||
|
||||
.ce-header p,
|
||||
.ce-header div{
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/** H e a d e r - settings */
|
||||
.ce_plugin_header--select_button{
|
||||
display: block;
|
||||
color: #306ac7;
|
||||
cursor: pointer;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
.ce_plugin_header--select_button:not(:last-of-type){
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
.ce_plugin_header--select_button:hover{
|
||||
color: #a1b4ec;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty header placeholder
|
||||
*/
|
||||
.ce-header:empty::before{
|
||||
content : attr(data-placeholder);
|
||||
color: #818BA1;
|
||||
opacity: .7;
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
.ce-header:focus::before{
|
||||
opacity: .1;
|
||||
* Styles for Plugin icon in Toolbar
|
||||
*/
|
||||
.cdx-header-icon {
|
||||
background-image: url('icon.svg');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,150 +1,324 @@
|
|||
/**
|
||||
* Example of making plugin
|
||||
* H e a d e r
|
||||
*/
|
||||
* @typedef {Object} HeaderData
|
||||
* @description Tool's input and output data format
|
||||
* @property {String} text — Header's content
|
||||
* @property {number} level - Header's level from 1 to 3
|
||||
*/
|
||||
|
||||
var header = (function (header_plugin) {
|
||||
/**
|
||||
* Header block for the CodeX Editor.
|
||||
*
|
||||
* @author CodeX Team (team@ifmo.su)
|
||||
* @copyright CodeX Team 2018
|
||||
* @license The MIT License (MIT)
|
||||
* @version 2.0.0
|
||||
*/
|
||||
class Header {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var methods_ = {
|
||||
|
||||
/**
|
||||
* Binds click event to passed button
|
||||
*/
|
||||
addSelectTypeClickListener : function (el, type) {
|
||||
el.addEventListener('click', function () {
|
||||
methods_.selectTypeClicked(type);
|
||||
}, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces old header with new type
|
||||
* @params {string} type - new header tagName: H1—H6
|
||||
*/
|
||||
selectTypeClicked : function (type) {
|
||||
var old_header, new_header;
|
||||
|
||||
/** Now current header stored as a currentNode */
|
||||
old_header = codex.editor.content.currentNode.querySelector('[contentEditable]');
|
||||
|
||||
/** Making new header */
|
||||
new_header = codex.editor.draw.node(type, [ 'ce-header' ], { innerHTML : old_header.innerHTML });
|
||||
new_header.contentEditable = true;
|
||||
new_header.setAttribute('data-placeholder', 'Заголовок');
|
||||
new_header.dataset.headerData = type;
|
||||
|
||||
codex.editor.content.switchBlock(old_header, new_header, 'header');
|
||||
|
||||
/** Close settings after replacing */
|
||||
codex.editor.toolbar.settings.close();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Make initial header block
|
||||
* @param {object} JSON with block data
|
||||
* @return {Element} element to append
|
||||
*/
|
||||
var make_ = function (data) {
|
||||
var availableTypes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
||||
tag,
|
||||
headerType = 'h2';
|
||||
|
||||
|
||||
if ( data && data['heading-styles'] && availableTypes.includes(data['heading-styles']) ) {
|
||||
headerType = data['heading-styles'];
|
||||
}
|
||||
|
||||
tag = document.createElement(headerType);
|
||||
|
||||
/**
|
||||
* Save header type in data-attr.
|
||||
* We need it in save method to extract type from HTML to JSON
|
||||
*/
|
||||
tag.dataset.headerData = headerType;
|
||||
|
||||
|
||||
if (data && data.text) {
|
||||
tag.textContent = data.text;
|
||||
}
|
||||
|
||||
if (!tag.dataset.headerData) {
|
||||
tag.dataset.headerData = 'h2';
|
||||
}
|
||||
|
||||
tag.classList.add('ce-header');
|
||||
tag.setAttribute('data-placeholder', 'Заголовок');
|
||||
tag.contentEditable = true;
|
||||
|
||||
return tag;
|
||||
};
|
||||
|
||||
header_plugin.prepareDataForSave = function (data) {
|
||||
|
||||
};
|
||||
* Should this tools be displayed at the Editor's Toolbox
|
||||
* @returns {boolean}
|
||||
* @public
|
||||
*/
|
||||
static get displayInToolbox() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to render HTML block from JSON
|
||||
*/
|
||||
header_plugin.render = function (data) {
|
||||
return make_(data);
|
||||
};
|
||||
* Class for the Toolbox icon
|
||||
* @returns {string}
|
||||
* @public
|
||||
*/
|
||||
static get iconClassName() {
|
||||
return 'cdx-header-icon';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to extract JSON data from HTML block
|
||||
* Render plugin`s main Element and fill it with saved data
|
||||
* @param {HeaderData} savedData — previously saved data
|
||||
*/
|
||||
constructor(savedData = {}) {
|
||||
/**
|
||||
* Styles
|
||||
* @type {Object}
|
||||
*/
|
||||
header_plugin.save = function (blockContent) {
|
||||
var data = {
|
||||
'heading-styles': blockContent.dataset.headerData,
|
||||
'format': 'html',
|
||||
'text': blockContent.textContent || ''
|
||||
this._CSS = {
|
||||
wrapper: 'ce-header',
|
||||
settingsButton: 'ce-settings__button',
|
||||
settingsSelected: 'ce-settings__button--selected',
|
||||
};
|
||||
|
||||
return data;
|
||||
};
|
||||
/**
|
||||
* Block's data
|
||||
* @type {Object}
|
||||
*/
|
||||
this._data = savedData || {};
|
||||
|
||||
/**
|
||||
* List of settings buttons
|
||||
* @type {HTMLElement[]}
|
||||
*/
|
||||
this.settingsButtons = [];
|
||||
|
||||
/**
|
||||
* Main Block wrapper
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
this._element = this.getTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings panel content
|
||||
* - - - - - - - - - - - - -
|
||||
* | настройки H1 H2 H3 |
|
||||
* - - - - - - - - - - - - -
|
||||
* @return {Element} element contains all settings
|
||||
*/
|
||||
header_plugin.makeSettings = function () {
|
||||
var holder = codex.editor.draw.node('DIV', [ 'cdx-plugin-settings--horisontal' ], {} ),
|
||||
types = {
|
||||
h2: 'H2',
|
||||
h3: 'H3',
|
||||
h4: 'H4'
|
||||
},
|
||||
selectTypeButton;
|
||||
* Return Tool's view
|
||||
* @returns {HTMLHeadingElement}
|
||||
* @public
|
||||
*/
|
||||
render() {
|
||||
return this._element;
|
||||
}
|
||||
|
||||
/** Now add type selectors */
|
||||
for (var type in types) {
|
||||
selectTypeButton = codex.editor.draw.node('SPAN', [ 'cdx-plugin-settings__item' ], { textContent : types[type] });
|
||||
methods_.addSelectTypeClickListener(selectTypeButton, type);
|
||||
/**
|
||||
* Create Block's settings block
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
makeSettings() {
|
||||
let holder = document.createElement('DIV');
|
||||
|
||||
/** Add type selectors */
|
||||
this.levels.forEach( level => {
|
||||
let selectTypeButton = document.createElement('SPAN');
|
||||
|
||||
selectTypeButton.classList.add(this._CSS.settingsButton);
|
||||
|
||||
/**
|
||||
* Highlight current level button
|
||||
*/
|
||||
if (this.currentLevel.number === level.number) {
|
||||
selectTypeButton.classList.add(this._CSS.settingsSelected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add SVG icon
|
||||
*/
|
||||
selectTypeButton.innerHTML = level.svg;
|
||||
|
||||
/**
|
||||
* Save level to its button
|
||||
*/
|
||||
selectTypeButton.dataset.level = level.number;
|
||||
|
||||
/**
|
||||
* Set up click handler
|
||||
*/
|
||||
selectTypeButton.addEventListener('click', () => {
|
||||
this.setLevel(level.number);
|
||||
});
|
||||
|
||||
/**
|
||||
* Append settings button to holder
|
||||
*/
|
||||
holder.appendChild(selectTypeButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings buttons
|
||||
*/
|
||||
this.settingsButtons.push(selectTypeButton);
|
||||
});
|
||||
|
||||
return holder;
|
||||
};
|
||||
}
|
||||
|
||||
header_plugin.validate = function (data) {
|
||||
if (data.text.trim() === '' || data['heading-styles'].trim() === '') {
|
||||
return false;
|
||||
/**
|
||||
* Callback for Block's settings buttons
|
||||
* @param level
|
||||
*/
|
||||
setLevel(level) {
|
||||
this.data = {
|
||||
level: level
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlight button by selected level
|
||||
*/
|
||||
this.settingsButtons.forEach(button => {
|
||||
button.classList.toggle(this._CSS.settingsSelected, parseInt(button.dataset.level) === level);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that specified how to merge two Text blocks.
|
||||
* Called by CodeX Editor by backspace at the beginning of the Block
|
||||
* @param {TextData} data
|
||||
* @public
|
||||
*/
|
||||
merge(data) {
|
||||
let newData = {
|
||||
text: this._data.text + data.text,
|
||||
level: this._data.level
|
||||
};
|
||||
|
||||
this.data = newData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Text block data:
|
||||
* - check for emptiness
|
||||
*
|
||||
* @param {TextData} savedData — data received after saving
|
||||
* @returns {boolean} false if saved data is not correct, otherwise true
|
||||
* @public
|
||||
*/
|
||||
validate(savedData) {
|
||||
return savedData.text.trim() !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract Tool's data from the view
|
||||
* @param {HTMLHeadingElement} toolsContent - Text tools rendered view
|
||||
* @returns {TextData} - saved data
|
||||
* @public
|
||||
*/
|
||||
save(toolsContent) {
|
||||
/**
|
||||
* @todo sanitize data
|
||||
*/
|
||||
|
||||
return {
|
||||
text: toolsContent.innerHTML,
|
||||
level: this.currentLevel.number
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current Tools`s data
|
||||
* @returns {TextData} Current data
|
||||
* @private
|
||||
*/
|
||||
get data() {
|
||||
this._data.text = this._element.innerHTML;
|
||||
this._data.level = this.currentLevel.number;
|
||||
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store data in plugin:
|
||||
* - at the this._data property
|
||||
* - at the HTML
|
||||
*
|
||||
* @param {HeaderData} data — data to set
|
||||
* @private
|
||||
*/
|
||||
set data(data) {
|
||||
this._data = data || {};
|
||||
|
||||
/**
|
||||
* If level is set and block in DOM
|
||||
* then replace it to a new block
|
||||
*/
|
||||
if (data.level !== undefined && this._element.parentNode) {
|
||||
/**
|
||||
* Create a new tag
|
||||
* @type {HTMLHeadingElement}
|
||||
*/
|
||||
let newHeader = this.getTag();
|
||||
|
||||
/**
|
||||
* Save Block's content
|
||||
*/
|
||||
newHeader.innerHTML = this._element.innerHTML;
|
||||
|
||||
/**
|
||||
* Replace blocks
|
||||
*/
|
||||
this._element.parentNode.replaceChild(newHeader, this._element);
|
||||
|
||||
/**
|
||||
* Save new block to private variable
|
||||
* @type {HTMLHeadingElement}
|
||||
* @private
|
||||
*/
|
||||
this._element = newHeader;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
/**
|
||||
* If data.text was passed then update block's content
|
||||
*/
|
||||
if (data.text !== undefined) {
|
||||
this._element.innerHTML = this._data.text || '';
|
||||
}
|
||||
}
|
||||
|
||||
header_plugin.destroy = function () {
|
||||
header = null;
|
||||
};
|
||||
/**
|
||||
* Get tag for target level
|
||||
* By default returns second-leveled header
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
getTag() {
|
||||
/**
|
||||
* Create element for current Block's level
|
||||
*/
|
||||
let tag = document.createElement(this.currentLevel.tag);
|
||||
|
||||
return header_plugin;
|
||||
})({});
|
||||
/**
|
||||
* Add text to block
|
||||
*/
|
||||
tag.innerHTML = this._data.text || '';
|
||||
|
||||
/**
|
||||
* Add styles class
|
||||
*/
|
||||
tag.classList.add(this._CSS.wrapper);
|
||||
|
||||
/**
|
||||
* Make tag editable
|
||||
*/
|
||||
tag.contentEditable = 'true';
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current level
|
||||
* @return {level}
|
||||
*/
|
||||
get currentLevel() {
|
||||
let level = this.levels.find( level => level.number === this._data.level);
|
||||
|
||||
if (!level) {
|
||||
level = this.levels[0];
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} level
|
||||
* @property {number} number - level number
|
||||
* @property {string} tag - tag correspondes with level number
|
||||
* @property {string} svg - icon
|
||||
*/
|
||||
|
||||
/**
|
||||
* Available header levels
|
||||
* @return {level[]}
|
||||
*/
|
||||
get levels() {
|
||||
return [
|
||||
{
|
||||
number: 2,
|
||||
tag: 'H2',
|
||||
svg: '<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm10.99 9.288h3.527c.351 0 .62.072.804.216.185.144.277.34.277.588 0 .22-.073.408-.22.56-.146.154-.368.23-.665.23h-4.972c-.338 0-.601-.093-.79-.28a.896.896 0 0 1-.284-.659c0-.162.06-.377.182-.645s.255-.478.399-.631a38.617 38.617 0 0 1 1.621-1.598c.482-.444.827-.735 1.034-.875.369-.261.676-.523.922-.787.245-.263.432-.534.56-.81.129-.278.193-.549.193-.815 0-.288-.069-.546-.206-.773a1.428 1.428 0 0 0-.56-.53 1.618 1.618 0 0 0-.774-.19c-.59 0-1.054.26-1.392.777-.045.068-.12.252-.226.554-.106.302-.225.534-.358.696-.133.162-.328.243-.585.243a.76.76 0 0 1-.56-.223c-.149-.148-.223-.351-.223-.608 0-.31.07-.635.21-.972.139-.338.347-.645.624-.92a3.093 3.093 0 0 1 1.054-.665c.426-.169.924-.253 1.496-.253.69 0 1.277.108 1.764.324.315.144.592.343.83.595.24.252.425.544.558.875.133.33.2.674.2 1.03 0 .558-.14 1.066-.416 1.523-.277.457-.56.815-.848 1.074-.288.26-.771.666-1.45 1.22-.677.554-1.142.984-1.394 1.29a3.836 3.836 0 0 0-.331.44z"/></svg>'
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
tag: 'H3',
|
||||
svg: '<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm11.61 4.919c.418 0 .778-.123 1.08-.368.301-.245.452-.597.452-1.055 0-.35-.12-.65-.36-.902-.241-.252-.566-.378-.974-.378-.277 0-.505.038-.684.116a1.1 1.1 0 0 0-.426.306 2.31 2.31 0 0 0-.296.49c-.093.2-.178.388-.255.565a.479.479 0 0 1-.245.225.965.965 0 0 1-.409.081.706.706 0 0 1-.5-.22c-.152-.148-.228-.345-.228-.59 0-.236.071-.484.214-.745a2.72 2.72 0 0 1 .627-.746 3.149 3.149 0 0 1 1.024-.568 4.122 4.122 0 0 1 1.368-.214c.44 0 .842.06 1.205.18.364.12.679.294.947.52.267.228.47.49.606.79.136.3.204.622.204.967 0 .454-.099.843-.296 1.168-.198.324-.48.64-.848.95.354.19.653.408.895.653.243.245.426.516.548.813.123.298.184.619.184.964 0 .413-.083.812-.248 1.198-.166.386-.41.73-.732 1.031a3.49 3.49 0 0 1-1.147.708c-.443.17-.932.256-1.467.256a3.512 3.512 0 0 1-1.464-.293 3.332 3.332 0 0 1-1.699-1.64c-.142-.314-.214-.573-.214-.777 0-.263.085-.475.255-.636a.89.89 0 0 1 .637-.242c.127 0 .25.037.367.112a.53.53 0 0 1 .232.27c.236.63.489 1.099.759 1.405.27.306.65.46 1.14.46a1.714 1.714 0 0 0 1.46-.824c.17-.273.256-.588.256-.947 0-.53-.145-.947-.436-1.249-.29-.302-.694-.453-1.212-.453-.09 0-.231.01-.422.028-.19.018-.313.027-.367.027-.25 0-.443-.062-.579-.187-.136-.125-.204-.299-.204-.521 0-.218.081-.394.245-.528.163-.134.406-.2.728-.2h.28z"/></svg>'
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
tag: 'H4',
|
||||
svg: '<svg width="20" height="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm13.003 10.09v-1.252h-3.38c-.427 0-.746-.097-.96-.29-.213-.193-.32-.456-.32-.788 0-.085.016-.171.048-.259.031-.088.078-.18.141-.276.063-.097.128-.19.195-.28.068-.09.15-.2.25-.33l3.568-4.774a5.44 5.44 0 0 1 .576-.683.763.763 0 0 1 .542-.212c.682 0 1.023.39 1.023 1.171v5.212h.29c.346 0 .623.047.832.142.208.094.313.3.313.62 0 .26-.086.45-.256.568-.17.12-.427.179-.768.179h-.41v1.252c0 .346-.077.603-.23.771-.152.168-.356.253-.612.253a.78.78 0 0 1-.61-.26c-.154-.173-.232-.427-.232-.764zm-2.895-2.76h2.895V4.91L12.26 8.823z"/></svg>'
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
example/plugins/header/icon.svg
Normal file
1
example/plugins/header/icon.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg width="11" height="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M7.6 8.15H2.25v4.525a1.125 1.125 0 0 1-2.25 0V1.125a1.125 1.125 0 1 1 2.25 0V5.9H7.6V1.125a1.125 1.125 0 0 1 2.25 0v11.55a1.125 1.125 0 0 1-2.25 0V8.15z"/></svg>
|
||||
|
After Width: | Height: | Size: 277 B |
|
|
@ -8,7 +8,7 @@
|
|||
* @version 2.0.1
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* @typedef {Object} TextData
|
||||
* @description Tool's input and output data format
|
||||
* @property {String} text — Paragraph's content. Can include HTML tags: <a><b><i>
|
||||
|
|
@ -20,7 +20,7 @@ class Text {
|
|||
* @public
|
||||
*/
|
||||
static get displayInToolbox() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -107,11 +107,13 @@ class Text {
|
|||
* @public
|
||||
*/
|
||||
save(toolsContent) {
|
||||
let toolData = {
|
||||
/**
|
||||
* @todo sanitize data
|
||||
*/
|
||||
|
||||
return {
|
||||
text: toolsContent.innerHTML
|
||||
};
|
||||
|
||||
return toolData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,10 +124,6 @@ class Text {
|
|||
get data() {
|
||||
let text = this._element.innerHTML;
|
||||
|
||||
/**
|
||||
* @todo sanitize data
|
||||
*/
|
||||
|
||||
this._data.text = text;
|
||||
|
||||
return this._data;
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@ export default class BlockManager extends Module {
|
|||
* @param {Object} block
|
||||
*/
|
||||
bindEvents(block) {
|
||||
this.Editor.Listeners.on(block.pluginsContent, 'keydown', (event) => this.Editor.BlockEvents.keydown(event));
|
||||
this.Editor.Listeners.on(block.pluginsContent, 'mouseup', (event) => this.Editor.BlockEvents.mouseUp(event));
|
||||
this.Editor.Listeners.on(block.pluginsContent, 'keyup', (event) => this.Editor.BlockEvents.keyup(event));
|
||||
this.Editor.Listeners.on(block.html, 'keydown', (event) => this.Editor.BlockEvents.keydown(event));
|
||||
this.Editor.Listeners.on(block.html, 'mouseup', (event) => this.Editor.BlockEvents.mouseUp(event));
|
||||
this.Editor.Listeners.on(block.html, 'keyup', (event) => this.Editor.BlockEvents.keyup(event));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default class Caret extends Module {
|
|||
* @param {Boolean} atEnd - put caret at the end of the text node or not
|
||||
*/
|
||||
setToBlock(block, offset = 0, atEnd = false) {
|
||||
let element = block.pluginsContent;
|
||||
let element = block.html;
|
||||
|
||||
/** If Element is INPUT */
|
||||
if ($.isNativeInput(element)) {
|
||||
|
|
@ -224,7 +224,7 @@ export default class Caret extends Module {
|
|||
|
||||
let selection = Selection.get(),
|
||||
anchorNode = selection.anchorNode,
|
||||
firstNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.pluginsContent);
|
||||
firstNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.html);
|
||||
|
||||
/**
|
||||
* Workaround case when caret in the text like " |Hello!"
|
||||
|
|
@ -276,7 +276,7 @@ export default class Caret extends Module {
|
|||
|
||||
let selection = Selection.get(),
|
||||
anchorNode = selection.anchorNode,
|
||||
lastNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.pluginsContent, true);
|
||||
lastNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.html, true);
|
||||
|
||||
/**
|
||||
* In case of
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@
|
|||
&__button {
|
||||
@apply --toolbar-button;
|
||||
|
||||
&--selected {
|
||||
color: var(--color-active-icon);
|
||||
}
|
||||
|
||||
&--delete {
|
||||
transition: background-color 300ms ease;
|
||||
will-change: background-color;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
display: inline-block;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
background: var(--bg-light);
|
||||
background-color: var(--bg-light);
|
||||
width: var(--toolbar-buttons-size);
|
||||
height: var(--toolbar-buttons-size);
|
||||
border-radius: 30px;
|
||||
|
|
@ -21,15 +21,15 @@
|
|||
text-align: center;
|
||||
line-height: var(--toolbar-buttons-size);
|
||||
|
||||
&::before {
|
||||
content: attr(title);
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 1em;
|
||||
font-variant-caps: all-small-caps;
|
||||
padding-left: 11.5px;
|
||||
margin-top: -1px;
|
||||
display: inline-block;
|
||||
}
|
||||
/*&::before {*/
|
||||
/*content: attr(title);*/
|
||||
/*font-size: 22px;*/
|
||||
/*font-weight: 500;*/
|
||||
/*letter-spacing: 1em;*/
|
||||
/*font-variant-caps: all-small-caps;*/
|
||||
/*padding-left: 11.5px;*/
|
||||
/*margin-top: -1px;*/
|
||||
/*display: inline-block;*/
|
||||
/*}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue