Skip to content

Commit

Permalink
Merge pull request #34 from editor-js/feature/read-only-mode
Browse files Browse the repository at this point in the history
Add read-only mode support
  • Loading branch information
neSpecc authored Sep 29, 2020
2 parents 0a0d73b + 955f293 commit 6a55636
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 48 deletions.
2 changes: 1 addition & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@editorjs/link",
"version": "2.2.1",
"version": "2.3.0",
"keywords": [
"codex editor",
"tool",
Expand Down
121 changes: 75 additions & 46 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

/**
* @typedef {Object} metaData
* @typedef {object} metaData
* @description Fetched link meta data
* @property {string} image - link's meta image
* @property {string} title - link's meta title
Expand All @@ -30,22 +30,32 @@ import polyfill from 'url-polyfill';
* title, description, image, url
*/
export default class LinkTool {
/**
* Notify core that read-only mode supported
*
* @returns {boolean}
*/
static get isReadOnlySupported() {
return true;
}

/**
* Get Tool toolbox settings
* icon - Tool icon's SVG
* title - title to show in toolbox
*
* @return {{icon: string, title: string}}
* @returns {{icon: string, title: string}}
*/
static get toolbox() {
return {
icon: ToolboxIcon,
title: 'Link'
title: 'Link',
};
}

/**
* Allow to press Enter inside the LinkTool input
*
* @returns {boolean}
* @public
*/
Expand All @@ -57,15 +67,17 @@ export default class LinkTool {
* @param {LinkToolData} data - previously saved data
* @param {config} config - user config for Tool
* @param {object} api - Editor.js API
* @param {boolean} readOnly - read-only mode flag
*/
constructor({ data, config, api }) {
constructor({ data, config, api, readOnly }) {
this.api = api;
this.readOnly = readOnly;

/**
* Tool's initial config
*/
this.config = {
endpoint: config.endpoint || ''
endpoint: config.endpoint || '',
};

this.nodes = {
Expand All @@ -78,22 +90,23 @@ export default class LinkTool {
linkImage: null,
linkTitle: null,
linkDescription: null,
linkText: null
linkText: null,
};

this._data = {
link: '',
meta: {}
meta: {},
};

this.data = data;
}

/**
* Renders Block content
*
* @public
*
* @return {HTMLDivElement}
* @returns {HTMLDivElement}
*/
render() {
this.nodes.wrapper = this.make('div', this.CSS.baseClass);
Expand All @@ -119,36 +132,38 @@ export default class LinkTool {

/**
* Return Block data
*
* @public
*
* @return {LinkToolData}
* @returns {LinkToolData}
*/
save() {
return this.data;
}

/**
* Stores all Tool's data
*
* @param {LinkToolData} data
*/
set data(data) {
this._data = Object.assign({}, {
link: data.link || this._data.link,
meta: data.meta || this._data.meta
meta: data.meta || this._data.meta,
});
}

/**
* Return Tool data
* @return {LinkToolData} data
*
* @returns {LinkToolData}
*/
get data() {
return this._data;
}

/**
* @return {object} - Link Tool styles
* @constructor
* @returns {object} - Link Tool styles
*/
get CSS() {
return {
Expand All @@ -170,46 +185,49 @@ export default class LinkTool {
linkText: 'link-tool__anchor',
progress: 'link-tool__progress',
progressLoading: 'link-tool__progress--loading',
progressLoaded: 'link-tool__progress--loaded'
progressLoaded: 'link-tool__progress--loaded',
};
}

/**
* Prepare input holder
* @return {HTMLElement} - url input
*
* @returns {HTMLElement}
*/
makeInputHolder() {
const inputHolder = this.make('div', this.CSS.inputHolder);

this.nodes.progress = this.make('label', this.CSS.progress);
this.nodes.input = this.make('div', [this.CSS.input, this.CSS.inputEl], {
contentEditable: true
contentEditable: !this.readOnly,
});

this.nodes.input.dataset.placeholder = this.api.i18n.t('Link');

this.nodes.input.addEventListener('paste', (event) => {
this.startFetching(event);
});

this.nodes.input.addEventListener('keydown', (event) => {
const [ENTER, A] = [13, 65];
const cmdPressed = event.ctrlKey || event.metaKey;

switch (event.keyCode) {
case ENTER:
event.preventDefault();
event.stopPropagation();

this.startFetching(event);
break;
case A:
if (cmdPressed) {
this.selectLinkUrl(event);
}
break;
}
});
if (!this.readOnly) {
this.nodes.input.addEventListener('paste', (event) => {
this.startFetching(event);
});

this.nodes.input.addEventListener('keydown', (event) => {
const [ENTER, A] = [13, 65];
const cmdPressed = event.ctrlKey || event.metaKey;

switch (event.keyCode) {
case ENTER:
event.preventDefault();
event.stopPropagation();

this.startFetching(event);
break;
case A:
if (cmdPressed) {
this.selectLinkUrl(event);
}
break;
}
});
}

inputHolder.appendChild(this.nodes.progress);
inputHolder.appendChild(this.nodes.input);
Expand All @@ -219,6 +237,8 @@ export default class LinkTool {

/**
* Activates link data fetching by url
*
* @param {PasteEvent} event
*/
startFetching(event) {
let url = this.nodes.input.textContent;
Expand All @@ -241,6 +261,7 @@ export default class LinkTool {

/**
* Select LinkTool input content by CMD+A
*
* @param {KeyboardEvent} event
*/
selectLinkUrl(event) {
Expand All @@ -262,12 +283,13 @@ export default class LinkTool {

/**
* Prepare link preview holder
* @return {HTMLElement}
*
* @returns {HTMLElement}
*/
prepareLinkPreview() {
const holder = this.make('a', this.CSS.linkContent, {
target: '_blank',
rel: 'nofollow noindex noreferrer'
rel: 'nofollow noindex noreferrer',
});

this.nodes.linkImage = this.make('div', this.CSS.linkImage);
Expand All @@ -280,6 +302,7 @@ export default class LinkTool {

/**
* Compose link preview from fetched data
*
* @param {metaData} meta - link meta data
*/
showLinkPreview({ image, title, description }) {
Expand Down Expand Up @@ -340,6 +363,7 @@ export default class LinkTool {

/**
* Sends to backend pasted url and receives link data
*
* @param {string} url - link source url
*/
async fetchLinkData(url) {
Expand All @@ -350,8 +374,8 @@ export default class LinkTool {
const { body } = await (ajax.get({
url: this.config.endpoint,
data: {
url
}
url,
},
}));

this.onFetch(body);
Expand All @@ -362,11 +386,13 @@ export default class LinkTool {

/**
* Link data fetching callback
*
* @param {UploadResponseFormat} response
*/
onFetch(response) {
if (!response || !response.success) {
this.fetchingFailed(this.api.i18n.t('Couldn\'t get this link data, try the other one'));

return;
}

Expand All @@ -376,6 +402,7 @@ export default class LinkTool {

if (!metaData) {
this.fetchingFailed(this.api.i18n.t('Wrong response format from the server'));

return;
}

Expand All @@ -387,36 +414,38 @@ export default class LinkTool {

/**
* Handle link fetching errors
*
* @private
*
* @param {string} errorMessage
*/
fetchingFailed(errorMessage) {
this.api.notifier.show({
message: errorMessage,
style: 'error'
style: 'error',
});

this.applyErrorStyle();
}

/**
* Helper method for elements creation
*
* @param tagName
* @param classNames
* @param attributes
* @return {HTMLElement}
* @returns {HTMLElement}
*/
make(tagName, classNames = null, attributes = {}) {
let el = document.createElement(tagName);
const el = document.createElement(tagName);

if (Array.isArray(classNames)) {
el.classList.add(...classNames);
} else if (classNames) {
el.classList.add(classNames);
}

for (let attrName in attributes) {
for (const attrName in attributes) {
el[attrName] = attributes[attrName];
}

Expand Down

0 comments on commit 6a55636

Please sign in to comment.