editor.js/src/components/modules/blockSelection.ts
George Berezhnoy 79f8660637 Version 2.13 (#719)
* Do not start multi-block selection on UI elements (#662)

* Do not start multi-block selection on UI elements

* Do not prevent mousedown event on inline toolbar actions

* Remove log

* Add comment

* Add link to issue

closes #646

* Fix loss of pointer (#666)

* Fix loss of pointer when click is outside of the editor but selection is inside

* Remove log

* Update shortcuts module (#685)

* Fixed possible grammatical typo (#681)

Thanks

* Update shortcuts module

* update changelog

* update

* Remove margin top for inline-link icon (#690)

* Remove margin top for inline-link icon

resolves #674

* Update CHANGELOG.md

* Remove unused style

* Pull fresh tools

* Remove changelog contents from readme (#700)

* #665 API to open and close inline-toolbar (#711)

* API to open and close inline-toolbar

* Fixed documentation

* renamed inline -> inline-toolbar

* removed dist

* reset editor.js

* added editor.js bundle

* Fixed build error

* Null checks on toolbar/inline@open

* updated bundle

* Improve some comments

* Updatd api.md CHANGELOG.md

* Change feature to new instead of improvement

* Allow holderId work with ref on dom element (#710)

* done

* update types

* attempt to simplify code

* remove useless helper

* revert holderId logic and add holder property

* Apply suggestions from code review

Co-Authored-By: dimensi <eddimensi@gmail.com>

* update holder type on string | HTMLElement

* fix typo

* add deprecated notice and fix typos

* fix wrong compare

* fix comments

* swap console.log on _.log

* update types for editor config

* update examples

* update docs

* update build

* Activating Open Collective (#736)

Hi, I'm making updates for Open Collective. Either you or a supporter signed this repo up for Open Collective. This pull request adds backers and sponsors from your Open Collective https://opencollective.com/editorjs❤️

It adds two badges at the top to show the latest number of backers and sponsors. It also adds placeholders so that the avatar/logo of new backers/sponsors can automatically be shown without having to update your README.md. [more info](https://github.com/opencollective/opencollective/wiki/Github-banner). See how it looks on this [repo](https://github.com/apex/apex#backers).

You can also add a postinstall script to let people know after npm|yarn install that you are welcoming donations (optional). [More info](https://github.com/OpenCollective/opencollective-cli)
You can also add a "Donate" button to your website and automatically show your backers and sponsors there with our widgets. Have a look here: https://opencollective.com/widgets

P.S: As with any pull request, feel free to comment or suggest changes. The only thing "required" are the placeholders on the README because we believe it's important to acknowledge the people in your community that are contributing (financially or with code!).

Thank you for your great contribution to the open source community. You are awesome! 🙌
And welcome to the open collective community! 😊

Come chat with us in the #opensource channel on https://slack.opencollective.com - great place to ask questions and share best practices with other open source sustainers!

* Do not install editor.js as dev-dependency (#731)

Resolves #730

* Move codex-notifier to dependencies for typescript declarations (#728)

* Close inline toolbar after creating new link by pressing ENTER (#722)

* Method to clear current selection and close inline toolbar

* clearSelection with optional collapsed range

* refactored selection.ts

* removed experimental function

* Update src/components/selection.ts

Co-Authored-By: tanmayv <12tanmayvijay@gmail.com>

* update version, add changelog

* Link Logo Image to homepage (#738)

* Update README.md (#744)

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

Co-Authored-By: neSpecc <specc.dev@gmail.com>

* Config minHeight option that allows to customize bottom zone (#745)

* issue-739: allow Block's editable element selection (#747)

* issue-739: allow Block's input selection

* little improvements

* update Changelog and cache inputs

* small fix

* delete map file

* fix inputs.count condition

* Fix typo in example paragraph (#749)

* Fix typo

* Update example-dev.html

* minor release
2019-04-29 15:52:54 +03:00

274 lines
6.2 KiB
TypeScript

/**
* @class BlockSelection
* @classdesc Manages Block selection with shortcut CMD+A
*
* @module BlockSelection
* @version 1.0.0
*/
import Module from '../__module';
import _ from '../utils';
import $ from '../dom';
import SelectionUtils from '../selection';
export default class BlockSelection extends Module {
/**
* Sanitizer Config
* @return {SanitizerConfig}
*/
private get sanitizerConfig() {
return {
p: {},
h1: {},
h2: {},
h3: {},
h4: {},
h5: {},
h6: {},
ol: {},
ul: {},
li: {},
br: true,
img: {
src: true,
width: true,
height: true,
},
a: {
href: true,
},
b: {},
i: {},
u: {},
};
}
/**
* Flag that identifies all Blocks selection
* @return {boolean}
*/
public get allBlocksSelected(): boolean {
const {BlockManager} = this.Editor;
return BlockManager.blocks.every((block) => block.selected === true);
}
/**
* Set selected all blocks
* @param {boolean} state
*/
public set allBlocksSelected(state: boolean) {
const {BlockManager} = this.Editor;
BlockManager.blocks.forEach((block) => block.selected = state);
}
/**
* Flag that identifies any Block selection
* @return {boolean}
*/
public get anyBlockSelected(): boolean {
const {BlockManager} = this.Editor;
return BlockManager.blocks.some((block) => block.selected === true);
}
/**
* Flag used to define block selection
* First CMD+A defines it as true and then second CMD+A selects all Blocks
* @type {boolean}
*/
private needToSelectAll: boolean = false;
/**
* Flag used to define native input selection
* In this case we allow double CMD+A to select Block
* @type {boolean}
*/
private nativeInputSelected: boolean = false;
/**
* Flag identifies any input selection
* That means we can select whole Block
* @type {boolean}
*/
private readyToBlockSelection: boolean = false;
/**
* SelectionUtils instance
* @type {SelectionUtils}
*/
private selection: SelectionUtils;
/**
* Module Preparation
* Registers Shortcuts CMD+A and CMD+C
* to select all and copy them
*/
public prepare(): void {
const {Shortcuts} = this.Editor;
/** Selection shortcut */
Shortcuts.add({
name: 'CMD+A',
handler: (event) => {
const {BlockManager} = this.Editor;
/**
* When one page consist of two or more EditorJS instances
* Shortcut module tries to handle all events. Thats why Editor's selection works inside the target Editor, but
* for others error occurs because nothing to select.
*
* Prevent such actions if focus is not inside the Editor
*/
if (!BlockManager.currentBlock) {
return;
}
this.handleCommandA(event);
},
});
this.selection = new SelectionUtils();
}
/**
* Remove selection of Block
* @param {number?} index - Block index according to the BlockManager's indexes
*/
public unSelectBlockByIndex(index?) {
const {BlockManager} = this.Editor;
let block;
if (isNaN(index)) {
block = BlockManager.currentBlock;
} else {
block = BlockManager.getBlockByIndex(index);
}
block.selected = false;
}
/**
* Clear selection from Blocks
*/
public clearSelection(restoreSelection = false) {
this.needToSelectAll = false;
this.nativeInputSelected = false;
this.readyToBlockSelection = false;
if (!this.anyBlockSelected || this.Editor.RectangleSelection.isRectActivated()) {
this.Editor.RectangleSelection.clearSelection();
return;
}
/**
* Restore selection when Block is already selected
* but someone tries to write something.
*/
if (restoreSelection) {
this.selection.restore();
}
/** Now all blocks cleared */
this.allBlocksSelected = false;
}
/**
* Reduce each Block and copy its content
*/
public copySelectedBlocks(): void {
const {BlockManager, Sanitizer} = this.Editor;
const fakeClipboard = $.make('div');
BlockManager.blocks.filter((block) => block.selected)
.forEach((block) => {
/**
* Make <p> tag that holds clean HTML
*/
const cleanHTML = Sanitizer.clean(block.holder.innerHTML, this.sanitizerConfig);
const fragment = $.make('p');
fragment.innerHTML = cleanHTML;
fakeClipboard.appendChild(fragment);
});
_.copyTextToClipboard(fakeClipboard.innerHTML);
}
/**
* select Block
* @param {number?} index - Block index according to the BlockManager's indexes
*/
public selectBlockByIndex(index?) {
const {BlockManager} = this.Editor;
/**
* Remove previous focused Block's state
*/
BlockManager.clearFocused();
let block;
if (isNaN(index)) {
block = BlockManager.currentBlock;
} else {
block = BlockManager.getBlockByIndex(index);
}
/** Save selection */
this.selection.save();
SelectionUtils.get()
.removeAllRanges();
block.selected = true;
}
/**
* First CMD+A Selects current focused blocks,
* and consequent second CMD+A keypress selects all blocks
*
* @param {keydown} event
*/
private handleCommandA(event): void {
this.Editor.RectangleSelection.clearSelection();
/** allow default selection on native inputs */
if ($.isNativeInput(event.target) && !this.nativeInputSelected) {
this.nativeInputSelected = true;
return;
}
const inputs = this.Editor.BlockManager.currentBlock.inputs;
/**
* If Block has more than one editable element allow native selection
* Second cmd+a will select whole Block
*/
if (inputs.length > 1 && !this.readyToBlockSelection) {
this.readyToBlockSelection = true;
return;
}
/** Prevent default selection */
event.preventDefault();
if (this.needToSelectAll) {
this.selectAllBlocks();
this.needToSelectAll = false;
} else {
this.selectBlockByIndex();
this.needToSelectAll = true;
}
}
/**
* Select All Blocks
* Each Block has selected setter that makes Block copyable
*/
private selectAllBlocks() {
this.allBlocksSelected = true;
}
}