diff --git a/README.md b/README.md index 35e25e4..4fa805f 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ npm i --save-dev @editorjs/link Include module at your application ```javascript -const LinkTool = require('@editorjs/link'); +const LinkTool = require("@editorjs/link"); ``` ### Download to your project's source dir @@ -67,34 +67,35 @@ const editor = EditorJS({ Link Tool supports these configuration parameters: -| Field | Type | Description | -| ---------|-------------|------------------------------------------------| -| endpoint | `string` | **Required:** the endpoint for link data fetching. | -| headers | `object` | **Optional:** the headers used in the GET request. | +| Field | Type | Description | +| ------------- | --------- | ---------------------------------------------------------- | +| endpoint | `string` | **Required:** the endpoint for link data fetching. | +| headers | `object` | **Optional:** the headers used in the GET request. | +| allowMetaEdit | `boolean` | **Optional:** Allow editing meta data after being fetched. | ## Output data This Tool returns `data` with following format -| Field | Type | Description | -| -------------- | --------- | ------------------------------- | -| link | `string` | Pasted link's url | -| meta | `object` | Fetched link's data. Any data got from the backend. Currently, the plugin's design supports the 'title', 'image', and 'description' fields. | +| Field | Type | Description | +| ----- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| link | `string` | Pasted link's url | +| meta | `object` | Fetched link's data. Any data got from the backend. Currently, the plugin's design supports the 'title', 'image', and 'description' fields. | ```json { - "type" : "linkTool", - "data" : { - "link" : "https://codex.so", - "meta" : { - "title" : "CodeX Team", - "site_name" : "CodeX", - "description" : "Club of web-development, design and marketing. We build team learning how to build full-valued projects on the world market.", - "image" : { - "url" : "https://codex.so/public/app/img/meta_img.png" - } - } + "type": "linkTool", + "data": { + "link": "https://codex.so", + "meta": { + "title": "CodeX Team", + "site_name": "CodeX", + "description": "Club of web-development, design and marketing. We build team learning how to build full-valued projects on the world market.", + "image": { + "url": "https://codex.so/public/app/img/meta_img.png" + } } + } } ``` @@ -107,11 +108,11 @@ Backend response **should** cover following format: ```json5 { - "success" : 1, - "link": "https://codex.so", // Optionally return a link to set the hyperlink URL - "meta": { - // ... any fields you want - } + success: 1, + link: "https://codex.so", // Optionally return a link to set the hyperlink URL + meta: { + // ... any fields you want + }, } ``` @@ -125,14 +126,14 @@ Currently, the plugin's design supports the 'title', 'image', and 'description' ```json5 { - "success" : 1, - "meta": { - "title" : "CodeX Team", - "description" : "Club of web-development, design and marketing. We build team learning how to build full-valued projects on the world market.", - "image" : { - "url" : "https://codex.so/public/app/img/meta_img.png" - } - } + success: 1, + meta: { + title: "CodeX Team", + description: "Club of web-development, design and marketing. We build team learning how to build full-valued projects on the world market.", + image: { + url: "https://codex.so/public/app/img/meta_img.png", + }, + }, } ``` @@ -144,6 +145,6 @@ Also, it can contain any additional fields you want to store. CodeX is a team of digital specialists around the world interested in building high-quality open source products on a global market. We are [open](https://codex.so/join) for young people who want to constantly improve their skills and grow professionally with experiments in cutting-edge technologies. -| 🌐 | Join 👋 | Twitter | Instagram | -| -- | -- | -- | -- | -| [codex.so](https://codex.so) | [codex.so/join](https://codex.so/join) |[@codex_team](http://twitter.com/codex_team) | [@codex_team](http://instagram.com/codex_team/) | +| 🌐 | Join 👋 | Twitter | Instagram | +| ---------------------------- | -------------------------------------- | -------------------------------------------- | ----------------------------------------------- | +| [codex.so](https://codex.so) | [codex.so/join](https://codex.so/join) | [@codex_team](http://twitter.com/codex_team) | [@codex_team](http://instagram.com/codex_team/) | diff --git a/src/index.css b/src/index.css index e066e10..3fc1f78 100644 --- a/src/index.css +++ b/src/index.css @@ -19,28 +19,35 @@ background-color: #fff3f6; border-color: #f3e0e0; color: #a95a5a; - box-shadow: inset 0 1px 3px 0 rgba(146, 62, 62, .05); + box-shadow: inset 0 1px 3px 0 rgba(146, 62, 62, 0.05); } } } + } + + [contentEditable="true"] { + &:focus { + outline: none; + } - &[contentEditable=true][data-placeholder]::before{ + &[data-placeholder]::before { position: absolute; content: attr(data-placeholder); color: #707684; font-weight: normal; opacity: 0; + cursor: text; + pointer-events: none; } - &[contentEditable=true][data-placeholder]:empty { - + &[data-placeholder]:empty { &::before { opacity: 1; } &:focus::before { - opacity: 0; - } + opacity: 0; + } } } @@ -79,13 +86,13 @@ &--rendered { background: #fff; border: 1px solid rgba(201, 201, 204, 0.48); - box-shadow: 0 1px 3px rgba(0,0,0, .1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); border-radius: 6px; will-change: filter; animation: link-in 450ms 1 cubic-bezier(0.215, 0.61, 0.355, 1); &:hover { - box-shadow: 0 0 3px rgba(0,0,0, .16); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.16); } } } @@ -123,7 +130,7 @@ } &__anchor { - display: block; + display: inline-block; font-size: 15px; line-height: 1em; color: #888 !important; diff --git a/src/index.js b/src/index.js index f380148..4c3cef4 100644 --- a/src/index.js +++ b/src/index.js @@ -17,6 +17,7 @@ * @typedef {object} LinkToolConfig * @property {string} endpoint - the endpoint for link data fetching * @property {object} headers - the headers used in the GET request + * @property {boolean} allowMetaEdit - allow editing meta data after being fetched. */ import './index.css'; @@ -64,7 +65,7 @@ export default class LinkTool { * @public */ static get enableLineBreaks() { - return true; + return false; } /** @@ -77,12 +78,14 @@ export default class LinkTool { constructor({ data, config, api, readOnly }) { this.api = api; this.readOnly = readOnly; + this.isDataSet = false; /** * Tool's initial config */ this.config = { endpoint: config.endpoint || '', + allowMetaEdit: config.allowMetaEdit || false, headers: config.headers || {}, }; @@ -111,7 +114,6 @@ export default class LinkTool { * Renders Block content * * @public - * * @returns {HTMLDivElement} */ render() { @@ -140,10 +142,14 @@ export default class LinkTool { * Return Block data * * @public - * * @returns {LinkToolData} */ save() { + if (this.config.allowMetaEdit && this.isDataSet) { + this.data.meta.title = this.nodes.linkTitle.innerHTML; + this.data.meta.description = this.nodes.linkDescription.innerHTML; + } + return this.data; } @@ -152,7 +158,6 @@ export default class LinkTool { * - check if given link is an empty string or not. * * @public - * * @returns {boolean} false if saved data is incorrect, otherwise true */ validate() { @@ -165,10 +170,13 @@ export default class LinkTool { * @param {LinkToolData} data - data to store */ set data(data) { - this._data = Object.assign({}, { - link: data.link || this._data.link, - meta: data.meta || this._data.meta, - }); + this._data = Object.assign( + {}, + { + link: data.link || this._data.link, + meta: data.meta || this._data.meta, + } + ); } /** @@ -305,15 +313,37 @@ export default class LinkTool { * @returns {HTMLElement} */ prepareLinkPreview() { - const holder = this.make('a', this.CSS.linkContent, { - target: '_blank', - rel: 'nofollow noindex noreferrer', - }); + let holder = null; + + // if edit meta is allowed add as dev + if (this.config.allowMetaEdit) { + holder = this.make('div', this.CSS.linkContent); + } else { + // add as link + holder = this.make('a', this.CSS.linkContent, { + target: '_blank', + rel: 'nofollow noindex noreferrer', + }); + } this.nodes.linkImage = this.make('div', this.CSS.linkImage); - this.nodes.linkTitle = this.make('div', this.CSS.linkTitle); - this.nodes.linkDescription = this.make('p', this.CSS.linkDescription); - this.nodes.linkText = this.make('span', this.CSS.linkText); + this.nodes.linkTitle = this.make('div', this.CSS.linkTitle, { + contentEditable: this.config.allowMetaEdit, + }); + this.nodes.linkDescription = this.make('p', this.CSS.linkDescription, { + contentEditable: this.config.allowMetaEdit, + }); + + // if edit meta is allowed add as link + if (this.config.allowMetaEdit) { + this.nodes.linkText = this.make('a', this.CSS.linkText, { + target: '_blank', + rel: 'nofollow noindex noreferrer', + }); + } else { + // add as span + this.nodes.linkText = this.make('span', this.CSS.linkText); + } return holder; } @@ -331,25 +361,40 @@ export default class LinkTool { this.nodes.linkContent.appendChild(this.nodes.linkImage); } - if (title) { - this.nodes.linkTitle.textContent = title; + if (title || this.config.allowMetaEdit) { + this.nodes.linkTitle.innerHTML = title; + this.nodes.linkTitle.dataset.placeholder = + this.api.i18n.t('Enter link title'); this.nodes.linkContent.appendChild(this.nodes.linkTitle); } - if (description) { - this.nodes.linkDescription.textContent = description; + if (description || this.config.allowMetaEdit) { + this.nodes.linkDescription.innerHTML = description; + this.nodes.linkDescription.dataset.placeholder = this.api.i18n.t( + 'Enter link description' + ); this.nodes.linkContent.appendChild(this.nodes.linkDescription); } this.nodes.linkContent.classList.add(this.CSS.linkContentRendered); - this.nodes.linkContent.setAttribute('href', this.data.link); + // if edit meta is not allowed add href to link content holder + if (!this.allowMetaEdit) { + this.nodes.linkContent.setAttribute('href', this.data.link); + } this.nodes.linkContent.appendChild(this.nodes.linkText); + // if edit meta is allowed add href to link text + if (this.config.allowMetaEdit) { + this.nodes.linkText.setAttribute('href', this.data.link); + } try { - this.nodes.linkText.textContent = (new URL(this.data.link)).hostname; + this.nodes.linkText.textContent = new URL(this.data.link).hostname; } catch (e) { this.nodes.linkText.textContent = this.data.link; } + + // Mark as data is set + this.isDataSet = true; } /** @@ -391,17 +436,17 @@ export default class LinkTool { this.data = { link: url }; try { - const { body } = await (ajax.get({ + const { body } = await ajax.get({ url: this.config.endpoint, headers: this.config.headers, data: { url, }, - })); + }); this.onFetch(body); } catch (error) { - this.fetchingFailed(this.api.i18n.t('Couldn\'t fetch the link data')); + this.fetchingFailed(this.api.i18n.t("Couldn't fetch the link data")); } } @@ -412,7 +457,9 @@ export default class LinkTool { */ onFetch(response) { if (!response || !response.success) { - this.fetchingFailed(this.api.i18n.t('Couldn\'t get this link data, try the other one')); + this.fetchingFailed( + this.api.i18n.t("Couldn't get this link data, try the other one") + ); return; } @@ -427,7 +474,9 @@ export default class LinkTool { }; if (!metaData) { - this.fetchingFailed(this.api.i18n.t('Wrong response format from the server')); + this.fetchingFailed( + this.api.i18n.t('Wrong response format from the server') + ); return; } @@ -442,7 +491,6 @@ export default class LinkTool { * Handle link fetching errors * * @private - * * @param {string} errorMessage - message to explain user what he should do */ fetchingFailed(errorMessage) {