@sertxudeveloper/markdown-editor
Version:
A customizable markdown editor for your projects
193 lines (155 loc) • 4.92 kB
text/typescript
import Editor from '../../Editor';
import { insertText } from '../../utils/Utils';
import BlockStyle from '../BlockStyle';
export type Image = {
url: string;
alt: string;
name: string;
};
export default class ImageBrowser {
editor: Editor;
element?: HTMLDivElement;
container?: HTMLDivElement;
images: Image[] = [];
constructor(editor: Editor) {
this.editor = editor;
}
/**
* Initialize the image browser
*
* @returns void
*/
init() {
this.element = document.createElement('div');
this.element.classList.add('markdown-image-browser');
this.element.addEventListener('click', (e) => {
if (e.target === this.element) this.onClose();
});
window.addEventListener('keydown', (e) => {
if (!this.element) return;
if (e.key === 'Escape') this.onClose();
});
this.container = document.createElement('div');
this.container.classList.add('browser-container');
}
/**
* Fetch the images from the endpoint and render the image browser
*
* @returns void
*/
browse() {
if (!this.editor.textarea) return;
const textarea = this.editor.textarea;
if (!this.element || !this.container) this.init();
this.container.innerHTML = '';
this.initHeader();
const grid = document.createElement('div');
grid.classList.add('browser-grid');
this.fetchImages().then((images: Image[]) => {
this.images = images;
this.render(grid);
});
this.element.appendChild(this.container);
document.body.appendChild(this.element);
}
/**
* Render the image browser
*
* @returns void
*/
render(grid: HTMLDivElement): void {
for (const image of this.images) {
let imageElement = this.renderImage(image);
grid.appendChild(imageElement);
}
this.container.appendChild(grid);
}
/**
* Initialize the header
*
* @returns void
*/
initHeader() {
const header = document.createElement('div');
header.classList.add('browser-header');
const title = document.createElement('span');
title.innerText = 'Select an image';
const close = document.createElement('button');
close.innerHTML = '×';
close.addEventListener('click', this.onClose.bind(this));
header.appendChild(title);
header.appendChild(close);
this.container.appendChild(header);
}
onClose() {
this.element.remove();
}
/**
* Render the provided image
*
* @param image Image to render
* @returns HTMLDivElement
*/
renderImage(image: Image): HTMLDivElement {
const container = document.createElement('div');
container.classList.add('browser-grid-item');
container.setAttribute('role', 'button');
const img = document.createElement('img');
img.src = image.url;
img.alt = image.alt;
const name = document.createElement('div');
name.innerText = image.name;
container.addEventListener('click', this.insertImage.bind(this, image));
container.appendChild(img);
container.appendChild(name);
return container;
}
/**
* Insert the selected image into the textarea
*
* @param image Image to insert
* @returns void
*/
insertImage(image: Image): void {
if (!this.editor.textarea) return;
const textarea = this.editor.textarea;
BlockStyle.applyStyle(textarea, {
prefix: ``,
suffix: '',
replaceNext: '',
scanFor: 'https?://',
surroundWithNewlines: true,
});
insertText(textarea, {
text: '\n',
selectionStart: textarea.selectionStart + 1,
selectionEnd: textarea.selectionStart + 1,
});
this.element.remove();
}
/**
* Get all the images from the provided endpoint
*
* @returns Promise<Image[]>
*/
fetchImages(): Promise<Image[]> {
return new Promise((resolve, reject) => {
fetch(this.editor.config.imageBrowserUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'X-Requested-With': 'XMLHttpRequest',
Referer: window.location.href,
},
})
.then((response) => response.json())
.then((json) => {
resolve(json);
})
.catch((error) => {
reject(error);
});
});
}
}