From f9b740c9c2277247d2d9db85a230632f6e2c2cdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=A5=E9=B9=8F=E5=B1=B1?= <121134388@qq.com>
Date: Wed, 9 Aug 2023 13:12:29 +0800
Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E9=80=9A=E8=BF=87ctrl=20+=20/=20?=
=?UTF-8?q?=E5=94=A4=E9=86=92=E5=B7=A5=E5=85=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/modules/blockEvents.ts | 2 +-
src/components/utils.ts | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/components/modules/blockEvents.ts b/src/components/modules/blockEvents.ts
index ee2d00c32..edc217fcf 100644
--- a/src/components/modules/blockEvents.ts
+++ b/src/components/modules/blockEvents.ts
@@ -49,7 +49,7 @@ export default class BlockEvents extends Module {
this.arrowLeftAndUp(event);
break;
- case _.keyCodes.TAB:
+ case event.ctrlKey && _.keyCodes.SLASH:
this.tabPressed(event);
break;
}
diff --git a/src/components/utils.ts b/src/components/utils.ts
index 6e30817b1..d9a2fb287 100644
--- a/src/components/utils.ts
+++ b/src/components/utils.ts
@@ -56,6 +56,7 @@ export const keyCodes = {
RIGHT: 39,
DELETE: 46,
META: 91,
+ SLASH:191,
};
/**
From ed2366a90d531283e9443de71e8cf24936a8f678 Mon Sep 17 00:00:00 2001
From: Asan <121134388@qq.com>
Date: Wed, 1 Nov 2023 12:53:24 +0800
Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E7=BC=A9=E8=BF=9B?=
=?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/modules/blockEvents.ts | 74 +++++++++++++++-------
src/components/modules/caret.ts | 90 +++++++++++++++++++++++++++
src/components/selection.ts | 2 +-
3 files changed, 143 insertions(+), 23 deletions(-)
diff --git a/src/components/modules/blockEvents.ts b/src/components/modules/blockEvents.ts
index edc217fcf..fd9eab57f 100644
--- a/src/components/modules/blockEvents.ts
+++ b/src/components/modules/blockEvents.ts
@@ -6,7 +6,7 @@ import * as _ from '../utils';
import SelectionUtils from '../selection';
import Flipper from '../flipper';
import type Block from '../block';
-import { areBlocksMergeable } from '../utils/blocks';
+import {areBlocksMergeable} from '../utils/blocks';
/**
*
@@ -38,7 +38,9 @@ export default class BlockEvents extends Module {
case _.keyCodes.ENTER:
this.enter(event);
break;
-
+ case _.keyCodes.TAB:
+ this.enter(event);
+ break;
case _.keyCodes.DOWN:
case _.keyCodes.RIGHT:
this.arrowRightAndDown(event);
@@ -49,7 +51,7 @@ export default class BlockEvents extends Module {
this.arrowLeftAndUp(event);
break;
- case event.ctrlKey && _.keyCodes.SLASH:
+ case (event.ctrlKey || event.metaKey) && _.keyCodes.SLASH:
this.tabPressed(event);
break;
}
@@ -124,7 +126,7 @@ export default class BlockEvents extends Module {
*/
this.Editor.BlockSelection.clearSelection(event);
- const { BlockManager, InlineToolbar, ConversionToolbar } = this.Editor;
+ const {BlockManager, InlineToolbar, ConversionToolbar} = this.Editor;
const currentBlock = BlockManager.currentBlock;
if (!currentBlock) {
@@ -176,7 +178,7 @@ export default class BlockEvents extends Module {
* @param {ClipboardEvent} event - clipboard event
*/
public handleCommandC(event: ClipboardEvent): void {
- const { BlockSelection } = this.Editor;
+ const {BlockSelection} = this.Editor;
if (!BlockSelection.anyBlockSelected) {
return;
@@ -192,7 +194,7 @@ export default class BlockEvents extends Module {
* @param {ClipboardEvent} event - clipboard event
*/
public handleCommandX(event: ClipboardEvent): void {
- const { BlockSelection, BlockManager, Caret } = this.Editor;
+ const {BlockSelection, BlockManager, Caret} = this.Editor;
if (!BlockSelection.anyBlockSelected) {
return;
@@ -219,7 +221,7 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keydown
*/
private enter(event: KeyboardEvent): void {
- const { BlockManager, UI } = this.Editor;
+ const {BlockManager, UI} = this.Editor;
const currentBlock = BlockManager.currentBlock;
/**
@@ -245,18 +247,28 @@ export default class BlockEvents extends Module {
return;
}
+ /**
+ * 处理普通文本首行tab
+ * @author asan
+ */
+ if (event.keyCode === _.keyCodes.TAB && currentBlock.name === 'paragraph') {
+ event.preventDefault();
+ return;
+ }
+
let newCurrent = this.Editor.BlockManager.currentBlock;
+
/**
* If enter has been pressed at the start of the text, just insert paragraph Block above
*/
if (this.Editor.Caret.isAtStart && !this.Editor.BlockManager.currentBlock.hasMedia) {
this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex);
- /**
- * If caret is at very end of the block, just append the new block without splitting
- * to prevent unnecessary dom mutation observing
- */
+ /**
+ * If caret is at very end of the block, just append the new block without splitting
+ * to prevent unnecessary dom mutation observing
+ */
} else if (this.Editor.Caret.isAtEnd) {
newCurrent = this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex + 1);
} else {
@@ -283,8 +295,16 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keydown
*/
private backspace(event: KeyboardEvent): void {
- const { BlockManager, Caret } = this.Editor;
- const { currentBlock, previousBlock } = BlockManager;
+ const {BlockManager, Caret} = this.Editor;
+ const {currentBlock, previousBlock} = BlockManager;
+
+
+ /**
+ * 文本在非首行首列时,不处理backspace事件
+ */
+ if (currentBlock.name === 'paragraph' && !Caret.isAtStartForText) {
+ return;
+ }
/**
* If some fragment is selected, leave native behaviour
@@ -366,9 +386,12 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keydown
*/
private delete(event: KeyboardEvent): void {
- const { BlockManager, Caret } = this.Editor;
- const { currentBlock, nextBlock } = BlockManager;
+ const {BlockManager, Caret} = this.Editor;
+ const {currentBlock, nextBlock} = BlockManager;
+ if (currentBlock.name === 'paragraph' && !Caret.isAtStartForText) {
+ return;
+ }
/**
* If some fragment is selected, leave native behaviour
*/
@@ -447,7 +470,7 @@ export default class BlockEvents extends Module {
* @param blockToMerge - what Block we want to merge
*/
private mergeBlocks(targetBlock: Block, blockToMerge: Block): void {
- const { BlockManager, Caret, Toolbar } = this.Editor;
+ const {BlockManager, Caret, Toolbar} = this.Editor;
Caret.createShadow(targetBlock.pluginsContent);
@@ -511,7 +534,7 @@ export default class BlockEvents extends Module {
if (this.Editor.BlockManager.currentBlock) {
this.Editor.BlockManager.currentBlock.updateCurrentInput();
}
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
}, 20)();
}
@@ -527,6 +550,13 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keyboard event
*/
private arrowLeftAndUp(event: KeyboardEvent): void {
+ const {BlockManager, Caret} = this.Editor;
+ const {currentBlock} = BlockManager;
+
+ if (currentBlock.name === 'paragraph' && !Caret.isAtStartForText) {
+ return;
+ }
+
/**
* Arrows might be handled on toolbars by flipper
* Check for Flipper.usedKeys to allow navigate by UP and disallow by LEFT
@@ -570,7 +600,7 @@ export default class BlockEvents extends Module {
if (this.Editor.BlockManager.currentBlock) {
this.Editor.BlockManager.currentBlock.updateCurrentInput();
}
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
}, 20)();
}
@@ -587,10 +617,10 @@ export default class BlockEvents extends Module {
*/
private needToolbarClosing(event: KeyboardEvent): boolean {
const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbar.toolbox.opened),
- blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened),
- inlineToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.InlineToolbar.opened),
- conversionToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.ConversionToolbar.opened),
- flippingToolbarItems = event.keyCode === _.keyCodes.TAB;
+ blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened),
+ inlineToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.InlineToolbar.opened),
+ conversionToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.ConversionToolbar.opened),
+ flippingToolbarItems = event.keyCode === _.keyCodes.TAB;
/**
* Do not close Toolbar in cases:
diff --git a/src/components/modules/caret.ts b/src/components/modules/caret.ts
index de64dc683..c5b376557 100644
--- a/src/components/modules/caret.ts
+++ b/src/components/modules/caret.ts
@@ -132,6 +132,96 @@ export default class Caret extends Module {
return firstNode === null || (focusNode === firstNode && focusOffset <= firstLetterPosition);
}
+ public get isAtStartForText(): boolean {
+ const selection = Selection.get();
+ if (selection['focusOffset'] !== 0) {
+ return false;
+ }
+ const firstNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.currentInput);
+ let focusNode = selection.focusNode;
+
+ /** In case lastNode is native input */
+ if ($.isNativeInput(firstNode)) {
+ return (firstNode as HTMLInputElement).selectionEnd === 0;
+ }
+
+ /** Case when selection have been cleared programmatically, for example after CBS */
+ if (!selection.anchorNode) {
+ return false;
+ }
+
+ /**
+ * Workaround case when caret in the text like " |Hello!"
+ * selection.anchorOffset is 1, but real caret visible position is 0
+ *
+ * @type {number}
+ */
+
+ let firstLetterPosition = focusNode.textContent.search(/\S/);
+
+ if (firstLetterPosition === -1) { // empty text
+ firstLetterPosition = 0;
+ }
+
+ /**
+ * If caret was set by external code, it might be set to text node wrapper.
+ *
|hello
<---- Selection references to instead of text node
+ *
+ * In this case, anchor node has ELEMENT_NODE node type.
+ * Anchor offset shows amount of children between start of the element and caret position.
+ *
+ * So we use child with focusOffset index as new anchorNode.
+ */
+ let focusOffset = selection.focusOffset;
+
+ if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) {
+ if (focusNode.childNodes[focusOffset]) {
+ focusNode = focusNode.childNodes[focusOffset];
+ focusOffset = 0;
+ } else {
+ focusNode = focusNode.childNodes[focusOffset - 1];
+ focusOffset = focusNode.textContent.length;
+ }
+ }
+
+ /**
+ * In case of
+ *
+ *
<-- first (and deepest) node is
+ * |adaddad <-- focus node
+ *
+ */
+ if ($.isLineBreakTag(firstNode as HTMLElement) || $.isEmpty(firstNode)) {
+ const leftSiblings = this.getHigherLevelSiblings(focusNode as HTMLElement, 'left');
+ const nothingAtLeft = leftSiblings.every((node) => {
+ /**
+ * Workaround case when block starts with several
's (created by SHIFT+ENTER)
+ *
+ * @see https://github.com/codex-team/editor.js/issues/726
+ * We need to allow to delete such line breaks, so in this case caret IS NOT AT START
+ */
+ const regularLineBreak = $.isLineBreakTag(node);
+ /**
+ * Workaround SHIFT+ENTER in Safari, that creates
instead of
+ */
+ const lineBreakInSafari = node.children.length === 1 && $.isLineBreakTag(node.children[0] as HTMLElement);
+ const isLineBreak = regularLineBreak || lineBreakInSafari;
+
+ return $.isEmpty(node) && !isLineBreak;
+ });
+
+ if (nothingAtLeft && focusOffset === firstLetterPosition) {
+ return true;
+ }
+ }
+
+ /**
+ * We use <= comparison for case:
+ * "| Hello" <--- selection.anchorOffset is 0, but firstLetterPosition is 1
+ */
+ return firstNode === null || (focusNode === firstNode && focusOffset <= firstLetterPosition);
+ }
+
/**
* Get's deepest last node and checks if offset is last node text length
*
diff --git a/src/components/selection.ts b/src/components/selection.ts
index fe5f961a8..6f1c7d2b8 100644
--- a/src/components/selection.ts
+++ b/src/components/selection.ts
@@ -368,7 +368,7 @@ export default class SelectionUtils {
}
/**
- * Adds fake cursor to the current range
+ * adds fake cursor to the current range
*/
public static addFakeCursor(): void {
const range = SelectionUtils.range;
From 8a68c6617b3c0dc474d8c7f2d630169e9d5a0a56 Mon Sep 17 00:00:00 2001
From: Asan <121134388@qq.com>
Date: Mon, 13 Nov 2023 17:02:05 +0800
Subject: [PATCH 3/4] =?UTF-8?q?feat:=E6=94=AF=E6=8C=81markdown=E5=AF=BC?=
=?UTF-8?q?=E5=87=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/block/api.ts | 4 +++
src/components/block/index.ts | 7 ++++
src/components/modules/paste.ts | 60 +++++++++++++++++----------------
src/components/modules/ui.ts | 52 +++++++++++++++++-----------
types/api/block.d.ts | 7 ++++
types/tools/block-tool.d.ts | 2 ++
6 files changed, 84 insertions(+), 48 deletions(-)
diff --git a/src/components/block/api.ts b/src/components/block/api.ts
index d760ab63e..6e97f6d11 100644
--- a/src/components/block/api.ts
+++ b/src/components/block/api.ts
@@ -104,6 +104,10 @@ function BlockAPI(
return block.save();
},
+ markdown(): string {
+ return block.markdown();
+ },
+
/**
* Validate Block data
*
diff --git a/src/components/block/index.ts b/src/components/block/index.ts
index b47fe7811..605ff47f6 100644
--- a/src/components/block/index.ts
+++ b/src/components/block/index.ts
@@ -620,6 +620,13 @@ export default class Block extends EventsDispatcher
{
return isValid;
}
+ public markdown(): string {
+ if (this.toolInstance.markdown instanceof Function) {
+ return this.toolInstance.markdown();
+ }
+ return '';
+ }
+
/**
* Returns data to render in tunes menu.
* Splits block tunes settings into 2 groups: popover items and custom html.
diff --git a/src/components/modules/paste.ts b/src/components/modules/paste.ts
index 2324a5d66..24afe2118 100644
--- a/src/components/modules/paste.ts
+++ b/src/components/modules/paste.ts
@@ -9,8 +9,8 @@ import {
SanitizerRule
} from '../../../types';
import Block from '../block';
-import { SavedData } from '../../../types/data-formats';
-import { clean, sanitizeBlocks } from '../utils/sanitizer';
+import {SavedData} from '../../../types/data-formats';
+import {clean, sanitizeBlocks} from '../utils/sanitizer';
import BlockTool from '../tools/block';
/**
@@ -165,13 +165,13 @@ export default class Paste extends Module {
* @param {boolean} isDragNDrop - true if data transfer comes from drag'n'drop events
*/
public async processDataTransfer(dataTransfer: DataTransfer, isDragNDrop = false): Promise {
- const { Tools } = this.Editor;
+ const {Tools} = this.Editor;
const types = dataTransfer.types;
/**
* In Microsoft Edge types is DOMStringList. So 'contains' is used to check if 'Files' type included
*/
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
const includesFiles = types.includes ? types.includes('Files') : (types as any).contains('Files');
if (includesFiles && !_.isEmpty(this.toolsFiles)) {
@@ -192,7 +192,8 @@ export default class Paste extends Module {
this.insertEditorJSData(JSON.parse(editorJSData));
return;
- } catch (e) { } // Do nothing and continue execution as usual if error appears
+ } catch (e) {
+ } // Do nothing and continue execution as usual if error appears
}
/**
@@ -213,7 +214,7 @@ export default class Paste extends Module {
return result;
}, {});
- const customConfig = Object.assign({}, toolsTags, Tools.getAllInlineToolsSanitizeConfig(), { br: {} });
+ const customConfig = Object.assign({}, toolsTags, Tools.getAllInlineToolsSanitizeConfig(), {br: {}});
const cleanData = clean(htmlData, customConfig);
/** If there is no HTML or HTML string is equal to plain one, process it as plain text */
@@ -231,7 +232,7 @@ export default class Paste extends Module {
* @param {boolean} isHTML - if passed string is HTML, this parameter should be true
*/
public async processText(data: string, isHTML = false): Promise {
- const { Caret, BlockManager } = this.Editor;
+ const {Caret, BlockManager} = this.Editor;
const dataToInsert = isHTML ? this.processHTML(data) : this.processPlain(data);
if (!dataToInsert.length) {
@@ -327,7 +328,7 @@ export default class Paste extends Module {
* If string, then it is a tag name.
*/
if (_.isString(tagOrSanitizeConfig)) {
- return [ tagOrSanitizeConfig ];
+ return [tagOrSanitizeConfig];
}
/**
* If object, then its keys are tags.
@@ -395,8 +396,8 @@ export default class Paste extends Module {
return;
}
- const { files = {} } = tool.pasteConfig;
- let { extensions, mimeTypes } = files;
+ const {files = {}} = tool.pasteConfig;
+ let {extensions, mimeTypes} = files;
if (!extensions && !mimeTypes) {
return;
@@ -477,16 +478,17 @@ export default class Paste extends Module {
* @param {ClipboardEvent} event - clipboard event
*/
private handlePasteEvent = async (event: ClipboardEvent): Promise => {
- const { BlockManager, Toolbar } = this.Editor;
+ const {BlockManager, Toolbar} = this.Editor;
/**
* When someone pasting into a block, its more stable to set current block by event target, instead of relying on current block set before
*/
const currentBlock = BlockManager.setCurrentBlockByChildNode(event.target as HTMLElement);
+
/** If target is native input or is not Block, use browser behaviour */
if (
- !currentBlock || (this.isNativeBehaviour(event.target) && !event.clipboardData.types.includes('Files'))
+ !currentBlock || currentBlock.name === 'threeWireMeter' || (this.isNativeBehaviour(event.target) && !event.clipboardData.types.includes('Files'))
) {
return;
}
@@ -511,7 +513,7 @@ export default class Paste extends Module {
* @param {FileList} items - pasted or dropped items
*/
private async processFiles(items: FileList): Promise {
- const { BlockManager } = this.Editor;
+ const {BlockManager} = this.Editor;
let dataToInsert: { type: string; event: PasteEvent }[];
@@ -543,7 +545,7 @@ export default class Paste extends Module {
const foundConfig = Object
.entries(this.toolsFiles)
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
- .find(([toolName, { mimeTypes, extensions } ]) => {
+ .find(([toolName, {mimeTypes, extensions}]) => {
const [fileType, fileSubtype] = file.type.split('/');
const foundExt = extensions.find((ext) => ext.toLowerCase() === extension.toLowerCase());
@@ -560,7 +562,7 @@ export default class Paste extends Module {
return;
}
- const [ tool ] = foundConfig;
+ const [tool] = foundConfig;
const pasteEvent = this.composePasteEvent('file', {
file,
});
@@ -578,7 +580,7 @@ export default class Paste extends Module {
* @returns {PasteData[]}
*/
private processHTML(innerHTML: string): PasteData[] {
- const { Tools } = this.Editor;
+ const {Tools} = this.Editor;
/**
* @todo Research, do we really need to always wrap innerHTML to a div:
@@ -622,7 +624,7 @@ export default class Paste extends Module {
/**
* Returns empty array if there is no paste config
*/
- const { tags: tagsOrSanitizeConfigs } = tool.pasteConfig || { tags: [] };
+ const {tags: tagsOrSanitizeConfigs} = tool.pasteConfig || {tags: []};
/**
* Reduce the tags or sanitize configs to a single array of sanitize config.
@@ -708,7 +710,7 @@ export default class Paste extends Module {
* @returns {PasteData[]}
*/
private processPlain(plain: string): PasteData[] {
- const { defaultBlock } = this.config as { defaultBlock: string };
+ const {defaultBlock} = this.config as { defaultBlock: string };
if (!plain) {
return [];
@@ -743,8 +745,8 @@ export default class Paste extends Module {
* @param {PasteData} dataToInsert - data of Block to insert
*/
private async processSingleBlock(dataToInsert: PasteData): Promise {
- const { Caret, BlockManager } = this.Editor;
- const { currentBlock } = BlockManager;
+ const {Caret, BlockManager} = this.Editor;
+ const {currentBlock} = BlockManager;
/**
* If pasted tool isn`t equal current Block or if pasted content contains block elements, insert it as new Block
@@ -771,8 +773,8 @@ export default class Paste extends Module {
* @param {PasteData} dataToInsert - data of Block to insert
*/
private async processInlinePaste(dataToInsert: PasteData): Promise {
- const { BlockManager, Caret } = this.Editor;
- const { content } = dataToInsert;
+ const {BlockManager, Caret} = this.Editor;
+ const {content} = dataToInsert;
const currentBlockIsDefault = BlockManager.currentBlock && BlockManager.currentBlock.tool.isDefault;
@@ -846,8 +848,8 @@ export default class Paste extends Module {
* @returns {void}
*/
private insertBlock(data: PasteData, canReplaceCurrentBlock = false): void {
- const { BlockManager, Caret } = this.Editor;
- const { currentBlock } = BlockManager;
+ const {BlockManager, Caret} = this.Editor;
+ const {currentBlock} = BlockManager;
let block: Block;
if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) {
@@ -869,12 +871,12 @@ export default class Paste extends Module {
* @returns {void}
*/
private insertEditorJSData(blocks: Pick[]): void {
- const { BlockManager, Caret, Tools } = this.Editor;
+ const {BlockManager, Caret, Tools} = this.Editor;
const sanitizedBlocks = sanitizeBlocks(blocks, (name) =>
Tools.blockTools.get(name).sanitizeConfig
);
- sanitizedBlocks.forEach(({ tool, data }, i) => {
+ sanitizedBlocks.forEach(({tool, data}, i) => {
let needToReplaceCurrentBlock = false;
if (i === 0) {
@@ -905,7 +907,7 @@ export default class Paste extends Module {
const element = node as HTMLElement;
- const { tool } = this.toolsTags[element.tagName] || {};
+ const {tool} = this.toolsTags[element.tagName] || {};
const toolTags = this.tagsByTool[tool?.name] || [];
const isSubstitutable = tags.includes(element.tagName);
@@ -913,11 +915,11 @@ export default class Paste extends Module {
const containsAnotherToolTags = Array
.from(element.children)
.some(
- ({ tagName }) => tags.includes(tagName) && !toolTags.includes(tagName)
+ ({tagName}) => tags.includes(tagName) && !toolTags.includes(tagName)
);
const containsBlockElements = Array.from(element.children).some(
- ({ tagName }) => $.blockElements.includes(tagName.toLowerCase())
+ ({tagName}) => $.blockElements.includes(tagName.toLowerCase())
);
/** Append inline elements to previous fragment */
diff --git a/src/components/modules/ui.ts b/src/components/modules/ui.ts
index 5bcc70512..bc651b2d9 100644
--- a/src/components/modules/ui.ts
+++ b/src/components/modules/ui.ts
@@ -11,10 +11,11 @@ import * as _ from '../utils';
import Selection from '../selection';
import Block from '../block';
import Flipper from '../flipper';
-import { mobileScreenBreakpoint } from '../utils';
+import {mobileScreenBreakpoint} from '../utils';
import styles from '../../styles/main.css?inline';
-import { BlockHovered } from '../events/BlockHovered';
+import {BlockHovered} from '../events/BlockHovered';
+
/**
* HTML Elements used for UI
*/
@@ -49,7 +50,7 @@ export default class UI extends Module {
public get CSS(): {
editorWrapper: string; editorWrapperNarrow: string; editorZone: string; editorZoneHidden: string;
editorEmpty: string; editorRtlFix: string;
- } {
+ } {
return {
editorWrapper: 'codex-editor',
editorWrapperNarrow: 'codex-editor--narrow',
@@ -110,7 +111,7 @@ export default class UI extends Module {
*/
private resizeDebouncer: () => void = _.debounce(() => {
this.windowResize();
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
}, 200);
/**
@@ -165,7 +166,7 @@ export default class UI extends Module {
* Check if Editor is empty and set CSS class to wrapper
*/
public checkEmptiness(): void {
- const { BlockManager } = this.Editor;
+ const {BlockManager} = this.Editor;
this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty, BlockManager.isEditorEmpty);
}
@@ -177,7 +178,7 @@ export default class UI extends Module {
* @returns {boolean}
*/
public get someToolbarOpened(): boolean {
- const { Toolbar, BlockSettings, InlineToolbar, ConversionToolbar } = this.Editor;
+ const {Toolbar, BlockSettings, InlineToolbar, ConversionToolbar} = this.Editor;
return BlockSettings.opened || InlineToolbar.opened || ConversionToolbar.opened || Toolbar.toolbox.opened;
}
@@ -216,7 +217,7 @@ export default class UI extends Module {
* Close all Editor's toolbars
*/
public closeAllToolbars(): void {
- const { Toolbar, BlockSettings, InlineToolbar, ConversionToolbar } = this.Editor;
+ const {Toolbar, BlockSettings, InlineToolbar, ConversionToolbar} = this.Editor;
BlockSettings.close();
InlineToolbar.close();
@@ -247,7 +248,7 @@ export default class UI extends Module {
*/
this.nodes.wrapper = $.make('div', [
this.CSS.editorWrapper,
- ...(this.isRtl ? [ this.CSS.editorRtlFix ] : []),
+ ...(this.isRtl ? [this.CSS.editorRtlFix] : []),
]);
this.nodes.redactor = $.make('div', this.CSS.editorZone);
@@ -276,7 +277,7 @@ export default class UI extends Module {
/**
* Load CSS
*/
- // eslint-disable-next-line @typescript-eslint/no-var-requires
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
const styleTagId = 'editor-js-styles';
/**
@@ -381,7 +382,7 @@ export default class UI extends Module {
this.eventsDispatcher.emit(BlockHovered, {
block: this.Editor.BlockManager.getBlockByChildNode(hoveredBlock),
});
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
}, 20), {
passive: true,
});
@@ -441,7 +442,7 @@ export default class UI extends Module {
* @param {KeyboardEvent} event - keyboard event
*/
private defaultBehaviour(event: KeyboardEvent): void {
- const { currentBlock } = this.Editor.BlockManager;
+ const {currentBlock} = this.Editor.BlockManager;
const keyDownOnEditor = (event.target as HTMLElement).closest(`.${this.CSS.editorWrapper}`);
const isMetaKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
@@ -476,7 +477,7 @@ export default class UI extends Module {
* @param {KeyboardEvent} event - keyboard event
*/
private backspacePressed(event: KeyboardEvent): void {
- const { BlockManager, BlockSelection, Caret } = this.Editor;
+ const {BlockManager, BlockSelection, Caret} = this.Editor;
/**
* If any block selected and selection doesn't exists on the page (that means no other editable element is focused),
@@ -535,7 +536,7 @@ export default class UI extends Module {
* @param {KeyboardEvent} event - keyboard event
*/
private enterPressed(event: KeyboardEvent): void {
- const { BlockManager, BlockSelection } = this.Editor;
+ const {BlockManager, BlockSelection} = this.Editor;
const hasPointerToBlock = BlockManager.currentBlockIndex >= 0;
/**
@@ -660,9 +661,22 @@ export default class UI extends Module {
* If click was fired on Editor`s wrapper, try to get clicked node by elementFromPoint method
*/
if (clickedNode === this.nodes.redactor) {
- const clientX = event instanceof MouseEvent ? event.clientX : event.touches[0].clientX;
- const clientY = event instanceof MouseEvent ? event.clientY : event.touches[0].clientY;
-
+ // let clientX = event instanceof MouseEvent ? event.clientX : event.touches[0].clientX;
+ // let clientY = event instanceof MouseEvent ? event.clientY : event.touches[0].clientY;
+ let clientX: number, clientY: number;
+
+ if (event instanceof MouseEvent) {
+ clientX = event.clientX;
+ clientY = event.clientY;
+ } else if (event.touches) {
+ if (event.touches.length > 1) {
+ clientX = event.touches[0].clientX;
+ clientY = event.touches[0].clientY;
+ }
+ } else if (event instanceof MouseEvent) {
+ clientX = event.clientX;
+ clientY = event.clientY;
+ }
clickedNode = document.elementFromPoint(clientX, clientY) as HTMLElement;
}
@@ -740,7 +754,7 @@ export default class UI extends Module {
const lastBlockBottomCoord = $.offset(lastBlock.holder).bottom;
const clickedCoord = event.pageY;
- const { BlockSelection } = this.Editor;
+ const {BlockSelection} = this.Editor;
const isClickedBottom = event.target instanceof Element &&
event.target.isEqualNode(this.nodes.redactor) &&
/**
@@ -757,7 +771,7 @@ export default class UI extends Module {
event.stopImmediatePropagation();
event.stopPropagation();
- const { BlockManager, Caret, Toolbar } = this.Editor;
+ const {BlockManager, Caret, Toolbar} = this.Editor;
/**
* Insert a default-block at the bottom if:
@@ -782,7 +796,7 @@ export default class UI extends Module {
* Uses for showing the Inline Toolbar
*/
private selectionChanged(): void {
- const { CrossBlockSelection, BlockSelection } = this.Editor;
+ const {CrossBlockSelection, BlockSelection} = this.Editor;
const focusedElement = Selection.anchorElement;
if (CrossBlockSelection.isCrossBlockSelectionStarted) {
diff --git a/types/api/block.d.ts b/types/api/block.d.ts
index c20e46222..2fdce90df 100644
--- a/types/api/block.d.ts
+++ b/types/api/block.d.ts
@@ -52,6 +52,13 @@ export interface BlockAPI {
*/
call(methodName: string, param?: object): void;
+ /**
+ * support markdown file export
+ * @return {string}
+ * @author asan
+ */
+ markdown(): string;
+
/**
* Save Block content
*
diff --git a/types/tools/block-tool.d.ts b/types/tools/block-tool.d.ts
index 8c9bb858a..bf18163fa 100644
--- a/types/tools/block-tool.d.ts
+++ b/types/tools/block-tool.d.ts
@@ -24,6 +24,8 @@ export interface BlockTool extends BaseTool {
*/
save(block: HTMLElement): BlockToolData;
+ markdown?(): string;
+
/**
* Create Block's settings block
*/
From 9602aa1e896e7d667a1a3a29aa797693a38b1a28 Mon Sep 17 00:00:00 2001
From: Asan <121134388@qq.com>
Date: Mon, 2 Sep 2024 09:47:01 +0800
Subject: [PATCH 4/4] feat:0902
---
src/components/block/api.ts | 10 ++++----
src/components/block/index.ts | 4 +--
src/components/modules/toolbar/index.ts | 33 +++++++++++++------------
types/api/block.d.ts | 2 +-
types/tools/block-tool.d.ts | 2 +-
5 files changed, 26 insertions(+), 25 deletions(-)
diff --git a/src/components/block/api.ts b/src/components/block/api.ts
index 6e97f6d11..35ce7c8c0 100644
--- a/src/components/block/api.ts
+++ b/src/components/block/api.ts
@@ -1,7 +1,7 @@
import Block from './index';
-import { BlockToolData, ToolConfig } from '../../../types/tools';
-import { SavedData } from '../../../types/data-formats';
-import { BlockAPI as BlockAPIInterface } from '../../../types/api';
+import {BlockToolData, ToolConfig} from '../../../types/tools';
+import {SavedData} from '../../../types/data-formats';
+import {BlockAPI as BlockAPIInterface} from '../../../types/api';
/**
* Constructs new BlockAPI object
@@ -100,11 +100,11 @@ function BlockAPI(
*
* @returns {Promise}
*/
- save(): Promise {
+ save(): Promise {
return block.save();
},
- markdown(): string {
+ markdown(): Promise {
return block.markdown();
},
diff --git a/src/components/block/index.ts b/src/components/block/index.ts
index 605ff47f6..a6de57953 100644
--- a/src/components/block/index.ts
+++ b/src/components/block/index.ts
@@ -620,11 +620,11 @@ export default class Block extends EventsDispatcher {
return isValid;
}
- public markdown(): string {
+ public markdown(): Promise {
if (this.toolInstance.markdown instanceof Function) {
return this.toolInstance.markdown();
}
- return '';
+ return null;
}
/**
diff --git a/src/components/modules/toolbar/index.ts b/src/components/modules/toolbar/index.ts
index 575a87865..d20c0ecd0 100644
--- a/src/components/modules/toolbar/index.ts
+++ b/src/components/modules/toolbar/index.ts
@@ -2,13 +2,13 @@ import Module from '../../__module';
import $ from '../../dom';
import * as _ from '../../utils';
import I18n from '../../i18n';
-import { I18nInternalNS } from '../../i18n/namespace-internal';
+import {I18nInternalNS} from '../../i18n/namespace-internal';
import * as tooltip from '../../utils/tooltip';
-import { ModuleConfig } from '../../../types-internal/module-config';
+import {ModuleConfig} from '../../../types-internal/module-config';
import Block from '../../block';
-import Toolbox, { ToolboxEvent } from '../../ui/toolbox';
-import { IconMenu, IconPlus } from '@codexteam/icons';
-import { BlockHovered } from '../../events/BlockHovered';
+import Toolbox, {ToolboxEvent} from '../../ui/toolbox';
+import {IconMenu, IconPlus} from '@codexteam/icons';
+import {BlockHovered} from '../../events/BlockHovered';
/**
* @todo Tab on non-empty block should open Block Settings of the hoveredBlock (not where caret is set)
@@ -39,6 +39,7 @@ interface ToolbarNodes {
plusButton: HTMLElement;
settingsToggler: HTMLElement;
}
+
/**
*
* «Toolbar» is the node that moves up/down over current block
@@ -108,7 +109,7 @@ export default class Toolbar extends Module {
* @param moduleConfiguration.config - Editor's config
* @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher
*/
- constructor({ config, eventsDispatcher }: ModuleConfig) {
+ constructor({config, eventsDispatcher}: ModuleConfig) {
super({
config,
eventsDispatcher,
@@ -155,7 +156,7 @@ export default class Toolbar extends Module {
open: () => void;
toggle: () => void;
hasFocus: () => boolean | undefined;
- } {
+ } {
return {
opened: this.toolboxInstance?.opened,
close: () => {
@@ -165,7 +166,7 @@ export default class Toolbar extends Module {
/**
* If Toolbox is not initialized yet, do nothing
*/
- if (this.toolboxInstance === null) {
+ if (this.toolboxInstance === null) {
_.log('toolbox.open() called before initialization is finished', 'warn');
return;
@@ -182,7 +183,7 @@ export default class Toolbar extends Module {
/**
* If Toolbox is not initialized yet, do nothing
*/
- if (this.toolboxInstance === null) {
+ if (this.toolboxInstance === null) {
_.log('toolbox.toggle() called before initialization is finished', 'warn');
return;
@@ -228,7 +229,7 @@ export default class Toolbar extends Module {
window.requestIdleCallback(() => {
this.drawUI();
this.enableModuleBindings();
- }, { timeout: 2000 });
+ }, {timeout: 2000});
} else {
this.destroy();
this.Editor.BlockSettings.destroy();
@@ -245,7 +246,7 @@ export default class Toolbar extends Module {
/**
* Some UI elements creates inside requestIdleCallback, so the can be not ready yet
*/
- if (this.toolboxInstance === null) {
+ if (this.toolboxInstance === null) {
_.log('Can\'t open Toolbar since Editor initialization is not finished yet', 'warn');
return;
@@ -272,7 +273,7 @@ export default class Toolbar extends Module {
this.hoveredBlock = block;
const targetBlockHolder = block.holder;
- const { isMobile } = this.Editor.UI;
+ const {isMobile} = this.Editor.UI;
const renderedContent = block.pluginsContent;
const renderedContentStyle = window.getComputedStyle(renderedContent);
const blockRenderedElementPaddingTop = parseInt(renderedContentStyle.paddingTop, 10);
@@ -390,9 +391,9 @@ export default class Toolbar extends Module {
*/
const tooltipContent = $.make('div');
- tooltipContent.appendChild(document.createTextNode(I18n.ui(I18nInternalNS.ui.toolbar.toolbox, 'Add')));
+ // tooltipContent.appendChild(document.createTextNode(I18n.ui(I18nInternalNS.ui.toolbar.toolbox, 'Add')));
tooltipContent.appendChild($.make('div', this.CSS.plusButtonShortcut, {
- textContent: '⇥ Tab',
+ textContent: _.beautifyShortcut('ctrl+/'),
}));
tooltip.onHover(this.nodes.plusButton, tooltipContent, {
@@ -455,8 +456,8 @@ export default class Toolbar extends Module {
this.Editor.UI.nodes.wrapper.classList.remove(this.CSS.openedToolboxHolderModifier);
});
- this.toolboxInstance.on(ToolboxEvent.BlockAdded, ({ block }) => {
- const { BlockManager, Caret } = this.Editor;
+ this.toolboxInstance.on(ToolboxEvent.BlockAdded, ({block}) => {
+ const {BlockManager, Caret} = this.Editor;
const newBlock = BlockManager.getBlockById(block.id);
/**
diff --git a/types/api/block.d.ts b/types/api/block.d.ts
index 2fdce90df..8f8fc246c 100644
--- a/types/api/block.d.ts
+++ b/types/api/block.d.ts
@@ -57,7 +57,7 @@ export interface BlockAPI {
* @return {string}
* @author asan
*/
- markdown(): string;
+ markdown(): Promise;
/**
* Save Block content
diff --git a/types/tools/block-tool.d.ts b/types/tools/block-tool.d.ts
index bf18163fa..e9bcc2775 100644
--- a/types/tools/block-tool.d.ts
+++ b/types/tools/block-tool.d.ts
@@ -24,7 +24,7 @@ export interface BlockTool extends BaseTool {
*/
save(block: HTMLElement): BlockToolData;
- markdown?(): string;
+ markdown?(): Promise;
/**
* Create Block's settings block