Paste/drop images directly to markdown editor
Ticket: 1135 - Changed `FileDrop` to only cover the upper 20% of the page, otherwise it will clash with markdown image drop.
This commit is contained in:
parent
e765a74eb0
commit
8d4a05157d
9 changed files with 73 additions and 11 deletions
|
@ -63,6 +63,7 @@
|
|||
"express": "^4.17.1",
|
||||
"humanize-duration": "^3.27.0",
|
||||
"if-env": "^1.0.4",
|
||||
"inline-attachment": "^2.0.3",
|
||||
"match-sorter": "^6.3.0",
|
||||
"parse-duration": "^1.0.0",
|
||||
"player.js": "^0.1.0",
|
||||
|
|
|
@ -2229,5 +2229,6 @@
|
|||
"Still Valid Until": "Still Valid Until",
|
||||
"Active channel": "Active channel",
|
||||
"This account has livestreaming disabled, please reach out to hello@odysee.com for assistance.": "This account has livestreaming disabled, please reach out to hello@odysee.com for assistance.",
|
||||
"Attach images by pasting or drag-and-drop.": "Attach images by pasting or drag-and-drop.",
|
||||
"--end--": "--end--"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// @flow
|
||||
import 'easymde/dist/easymde.min.css';
|
||||
|
||||
import 'inline-attachment/src/inline-attachment';
|
||||
import 'inline-attachment/src/codemirror-4.inline-attachment';
|
||||
import { IMG_CDN_PUBLISH_URL, JSON_RESPONSE_KEYS, UPLOAD_CONFIG } from 'constants/cdn_urls';
|
||||
import { FF_MAX_CHARS_DEFAULT } from 'constants/form-field';
|
||||
import { openEditorMenu, stopContextMenu } from 'util/context-menu';
|
||||
import { lazyImport } from 'util/lazyImport';
|
||||
|
@ -179,8 +182,8 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
// SimpleMDE max char check
|
||||
editor.codemirror.on('beforeChange', (instance, changes) => {
|
||||
if (textAreaMaxLength && changes.update) {
|
||||
var str = changes.text.join('\n');
|
||||
var delta = str.length - (instance.indexFromPos(changes.to) - instance.indexFromPos(changes.from));
|
||||
let str = changes.text.join('\n');
|
||||
let delta = str.length - (instance.indexFromPos(changes.to) - instance.indexFromPos(changes.from));
|
||||
|
||||
if (delta <= 0) return;
|
||||
|
||||
|
@ -227,6 +230,16 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
}
|
||||
} catch (e) {} // Do nothing (revert to original behavior)
|
||||
});
|
||||
|
||||
// Add ability to upload pasted/dragged image (https://github.com/sparksuite/simplemde-markdown-editor/issues/328#issuecomment-227075500)
|
||||
window.inlineAttachment.editors.codemirror4.attach(editor.codemirror, {
|
||||
uploadUrl: IMG_CDN_PUBLISH_URL,
|
||||
uploadFieldName: UPLOAD_CONFIG.BLOB_KEY,
|
||||
extraParams: { [UPLOAD_CONFIG.ACTION_KEY]: UPLOAD_CONFIG.ACTION_VAL },
|
||||
filenameTag: '{filename}',
|
||||
urlText: '![image]({filename})',
|
||||
jsonFieldName: JSON_RESPONSE_KEYS.UPLOADED_URL,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -250,6 +263,17 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
options={{
|
||||
spellChecker: true,
|
||||
hideIcons: ['heading', 'image', 'fullscreen', 'side-by-side'],
|
||||
status: [
|
||||
{
|
||||
className: 'editor-statusbar__upload-hint',
|
||||
defaultValue: (el) => {
|
||||
el.innerHTML = __('Attach images by pasting or drag-and-drop.');
|
||||
},
|
||||
},
|
||||
'lines',
|
||||
'words',
|
||||
'cursor',
|
||||
],
|
||||
previewRender(plainText) {
|
||||
const preview = <MarkdownPreview content={plainText} noDataStore />;
|
||||
return ReactDOMServer.renderToString(preview);
|
||||
|
|
|
@ -115,8 +115,12 @@ function FileDrop(props: Props) {
|
|||
|
||||
// Handle file drop...
|
||||
React.useEffect(() => {
|
||||
if (dropData && !files.length && (!modal || modal.id !== MODALS.FILE_SELECTION)) {
|
||||
getTree(dropData)
|
||||
const DROP_AREA_HEIGHT_PCT = 0.2; // @see: css[.file-drop -> height]
|
||||
const windowHeight = window.innerHeight || document.documentElement?.clientHeight || 768;
|
||||
const dropAreaBottom = windowHeight * DROP_AREA_HEIGHT_PCT;
|
||||
|
||||
if (dropData && dropData.y <= dropAreaBottom && !files.length && (!modal || modal.id !== MODALS.FILE_SELECTION)) {
|
||||
getTree(dropData.dataTransfer)
|
||||
.then((entries) => {
|
||||
if (entries && entries.length) {
|
||||
setFiles(entries);
|
||||
|
|
|
@ -1,2 +1,20 @@
|
|||
export const IMG_CDN_PUBLISH_URL = 'https://thumbs.odycdn.com/upload.php';
|
||||
export const IMG_CDN_STATUS_URL = 'https://thumbs.odycdn.com/status.php';
|
||||
|
||||
export const JSON_RESPONSE_KEYS = Object.freeze({
|
||||
STATUS: 'type',
|
||||
UPLOADED_URL: 'message',
|
||||
TIMESTAMP: 'timestamp',
|
||||
// --- Sample response ---
|
||||
// {
|
||||
// "type": "success",
|
||||
// "message": "https://thumbs.odycdn.com/90c08452a2e2ebb347c8c95f749a84c5.png",
|
||||
// "timestamp": 1648731440
|
||||
// }
|
||||
});
|
||||
|
||||
export const UPLOAD_CONFIG = Object.freeze({
|
||||
BLOB_KEY: 'file-input',
|
||||
ACTION_KEY: 'upload',
|
||||
ACTION_VAL: 'Upload',
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ const DRAG_STATE = {
|
|||
};
|
||||
|
||||
// Returns simple detection for global drag-drop
|
||||
export default function useFetched() {
|
||||
export default function useDragDrop() {
|
||||
const [drag, setDrag] = React.useState(false);
|
||||
const [dropData, setDropData] = React.useState(null);
|
||||
|
||||
|
@ -32,7 +32,7 @@ export default function useFetched() {
|
|||
let draggingElement = false;
|
||||
|
||||
// Handle file drop
|
||||
const handleDropEvent = event => {
|
||||
const handleDropEvent = (event) => {
|
||||
// Ignore non file types ( html elements / text )
|
||||
if (!draggingElement) {
|
||||
event.stopPropagation();
|
||||
|
@ -41,7 +41,7 @@ export default function useFetched() {
|
|||
const files = event.dataTransfer.files;
|
||||
// Check for files
|
||||
if (files.length > 0) {
|
||||
setDropData(event.dataTransfer);
|
||||
setDropData(event);
|
||||
}
|
||||
}
|
||||
// Reset state ( hide drop zone )
|
||||
|
@ -50,12 +50,12 @@ export default function useFetched() {
|
|||
};
|
||||
|
||||
// Drag event for non files type ( html elements / text )
|
||||
const handleDragElementEvent = event => {
|
||||
const handleDragElementEvent = (event) => {
|
||||
draggingElement = DRAG_STATE[event.type];
|
||||
};
|
||||
|
||||
// Drag events
|
||||
const handleDragEvent = event => {
|
||||
const handleDragEvent = (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
// Prevent multiple drop areas
|
||||
|
@ -71,7 +71,7 @@ export default function useFetched() {
|
|||
};
|
||||
|
||||
// Register / Unregister listeners
|
||||
const handleEventListeners = event => {
|
||||
const handleEventListeners = (event) => {
|
||||
const action = `${event}EventListener`;
|
||||
// Handle drop event
|
||||
document[action]('drop', handleDropEvent);
|
||||
|
@ -82,6 +82,7 @@ export default function useFetched() {
|
|||
document[action](DRAG_TYPES.END, handleDragElementEvent);
|
||||
document[action](DRAG_TYPES.START, handleDragElementEvent);
|
||||
};
|
||||
|
||||
// On component mounted:
|
||||
// Register event listeners
|
||||
handleEventListeners(LISTENER.ADD);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
position: fixed;
|
||||
background: var(--color-background-overlay);
|
||||
opacity: 0;
|
||||
height: 100%;
|
||||
height: 20%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
|
|
|
@ -52,6 +52,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.editor-statusbar {
|
||||
.editor-statusbar__upload-hint {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.form-field--SimpleMDE {
|
||||
margin-top: var(--spacing-l);
|
||||
|
||||
|
|
|
@ -9234,6 +9234,11 @@ ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
|||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
|
||||
integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
|
||||
|
||||
inline-attachment@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inline-attachment/-/inline-attachment-2.0.3.tgz#5ee32374583fabd3b7206df2e20f251ba20c4306"
|
||||
integrity sha1-XuMjdFg/q9O3IG3y4g8lG6IMQwY=
|
||||
|
||||
inline-style-parser@0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
|
||||
|
|
Loading…
Reference in a new issue