Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat (base) implemented new methods #11

Merged
merged 17 commits into from
Aug 16, 2024
Binary file modified .yarn/install-state.gz
Binary file not shown.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@
"build:caret": "cd packages/caret && yarn build",
"build:keyboard": "cd packages/keyboard && yarn build",
"lint": "eslint",
"lint:fix": "yarn lint --fix"
"lint:fix": "yarn lint --fix",
"generate-docs": "ts-node scripts/generateReadme.ts"
},
"devDependencies": {
"@types/fs-extra": "^11",
"@types/node": "^20.10.7",
"eslint": "^9.0.0",
"eslint-config-codex": "^2.0.0",
"fs-extra": "^11.2.0",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"typescript-eslint": "^7.9.0"
},
Expand Down
23 changes: 23 additions & 0 deletions packages/caret/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# @editorjs/caret
Utils useful for work with caret for Editor.js tools development
### Installation
```
npm install @editorjs/caret
```
### Function list
- [checkContenteditableSliceForEmptiness](https://github.com/editor-js/utils/blob/main/packages/caret/src/checkContenteditableSliceForEmptiness.ts) - Checks content at left or right of the passed node for emptiness.
- [focus](https://github.com/editor-js/utils/blob/main/packages/caret/src/focus.ts) - Set focus to contenteditable or native input element
- [getCaretNodeAndOffset](https://github.com/editor-js/utils/blob/main/packages/caret/src/getCaretNodeAndOffset.ts) - Returns TextNode containing a caret and a caret offset in it
- [getContenteditableSlice](https://github.com/editor-js/utils/blob/main/packages/caret/src/getContenteditableSlice.ts) - Returns slice of the contenteditable html element from caret position to the start or end (depending on direction)
- [getRange](https://github.com/editor-js/utils/blob/main/packages/caret/src/getRange.ts) - Returns the first range
- [isCaretAtEndOfInput](https://github.com/editor-js/utils/blob/main/packages/caret/src/isCaretAtEndOfInput.ts) - Checks if caret is at the end of the passed input
- [isCaretAtStartOfInput](https://github.com/editor-js/utils/blob/main/packages/caret/src/isCaretAtStartOfInput.ts) - Checks if caret is at the start of the passed input
- [save](https://github.com/editor-js/utils/blob/main/packages/caret/src/save.ts) - Saves caret position using hidden <span>
# About CodeX
<img align="right" width="120" height="120" src="https://codex.so/public/app/img/codex-logo.svg" hspace="50">

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/) |
3 changes: 2 additions & 1 deletion packages/caret/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@editorjs/caret",
"version": "0.0.1",
"description": "Utils useful for work with caret for Editor.js tools development",
"version": "0.0.2",
"main": "dist/index.js",
"license": "MIT",
"scripts": {
Expand Down
22 changes: 22 additions & 0 deletions packages/caret/src/checkContenteditableSliceForEmptiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { isCollapsedWhitespaces } from '@editorjs/dom';
import { getContenteditableSlice } from './getContenteditableSlice';

/**
* Checks content at left or right of the passed node for emptiness.
* @param contenteditable - The contenteditable element containing the nodes.
* @param fromNode - The starting node to check from.
* @param offsetInsideNode - The offset inside the starting node.
* @param direction - The direction to check ('left' or 'right').
* @returns true if adjacent content is empty, false otherwise.
*/
export function checkContenteditableSliceForEmptiness(contenteditable: HTMLElement, fromNode: Node, offsetInsideNode: number, direction: 'left' | 'right'): boolean {
/**
* Get content editable slice
*/
const textContent = getContenteditableSlice(contenteditable, fromNode, offsetInsideNode, direction);

/**
* Check extracted slice for emptiness
*/
return isCollapsedWhitespaces(textContent);
}
29 changes: 29 additions & 0 deletions packages/caret/src/focus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { isNativeInput } from '@editorjs/dom';

/**
* Set focus to contenteditable or native input element
* @param element - element where to set focus
* @param atStart - where to set focus: at the start or at the end
*/
export function focus(element: HTMLElement, atStart: boolean = true): void {
/** If element is native input */
if (isNativeInput(element)) {
element.focus();
const position = atStart ? 0 : element.value.length;

element.setSelectionRange(position, position);
} else {
const range = document.createRange();
const selection = window.getSelection();

if (!selection) {
return;
}

range.selectNodeContents(element);
range.collapse(atStart);

selection.removeAllRanges();
selection.addRange(range);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { isCollapsedWhitespaces } from './isCollapsedWhitespaces';
import { fragmentToString } from '@editorjs/dom';

/**
* Checks content at left or right of the passed node for emptiness.
* Returns slice of the contenteditable html element from caret position to the start or end (depending on direction)
* @param contenteditable - The contenteditable element containing the nodes.
* @param fromNode - The starting node to check from.
* @param offsetInsideNode - The offset inside the starting node.
* @param direction - The direction to check ('left' or 'right').
* @param extract - should we remove from element extracted part
* @returns true if adjacent content is empty, false otherwise.
*/
export function checkContenteditableSliceForEmptiness(contenteditable: HTMLElement, fromNode: Node, offsetInsideNode: number, direction: 'left' | 'right'): boolean {
export function getContenteditableSlice(contenteditable: HTMLElement, fromNode: Node, offsetInsideNode: number, direction: 'left' | 'right', extract: boolean = false): string {
const range = document.createRange();

/**
Expand All @@ -28,6 +29,15 @@ export function checkContenteditableSliceForEmptiness(contenteditable: HTMLEleme
range.setEnd(contenteditable, contenteditable.childNodes.length);
}

/**
* Check if we should extract content from the range
*/
if (extract === true) {
const textContent = range.extractContents();

return fragmentToString(textContent);
}

/**
* Clone the range's content and check its text content
*/
Expand All @@ -45,5 +55,5 @@ export function checkContenteditableSliceForEmptiness(contenteditable: HTMLEleme
*
* If text contains only invisible whitespaces, it is considered to be empty
*/
return isCollapsedWhitespaces(textContent);
return textContent;
}
9 changes: 9 additions & 0 deletions packages/caret/src/getRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Returns the first range
* @returns range of the caret if it exists, null otherwise
*/
export function getRange(): Range | null {
const selection = window.getSelection();

return selection && selection.rangeCount ? selection.getRangeAt(0) : null;
}
14 changes: 12 additions & 2 deletions packages/caret/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { checkContenteditableSliceForEmptiness } from './checkContenteditableSliceForEmptiness';
import { getContenteditableSlice } from './getContenteditableSlice';
import { focus } from './focus';
import { getCaretNodeAndOffset } from './getCaretNodeAndOffset';
import { getRange } from './getRange';
import { isCaretAtEndOfInput } from './isCaretAtEndOfInput';
import { isCaretAtStartOfInput } from './isCaretAtStartOfInput';
import { save } from './save';

export { getCaretNodeAndOffset,
export { checkContenteditableSliceForEmptiness,
getContenteditableSlice,
focus,
getCaretNodeAndOffset,
getRange,
isCaretAtEndOfInput,
isCaretAtStartOfInput };
isCaretAtStartOfInput,
save };
4 changes: 3 additions & 1 deletion packages/caret/src/isCaretAtEndOfInput.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { getDeepestNode, isNativeInput, checkContenteditableSliceForEmptiness } from '@editorjs/dom';
import { getDeepestNode, isNativeInput } from '@editorjs/dom';
import { getCaretNodeAndOffset } from './getCaretNodeAndOffset';
import { checkContenteditableSliceForEmptiness } from './checkContenteditableSliceForEmptiness';

/**
* Checks if caret is at the end of the passed input
*
Expand Down
3 changes: 2 additions & 1 deletion packages/caret/src/isCaretAtStartOfInput.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getDeepestNode, isEmpty, isNativeInput, checkContenteditableSliceForEmptiness } from '@editorjs/dom';
import { getDeepestNode, isEmpty, isNativeInput } from '@editorjs/dom';
import { getCaretNodeAndOffset } from './getCaretNodeAndOffset';
import { checkContenteditableSliceForEmptiness } from './checkContenteditableSliceForEmptiness';

/**
* Checks if caret is at the start of the passed input
Expand Down
45 changes: 45 additions & 0 deletions packages/caret/src/save.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { make } from '@editorjs/dom';
import { getRange } from './getRange';

/**
* Saves caret position using hidden <span>
* @returns function for resoring the caret
*/
export function save(): () => void {
const range = getRange();
const caret = make('span');

caret.id = 'cursor';

caret.hidden = true;

if (!range) {
return;
}
range.insertNode(caret);

/**
* Return funciton that will restore caret and delete temporary span element
*/
return function restore(): void {
const sel = window.getSelection();

if (!sel) {
return;
}

range.setStartAfter(caret);
range.setEndAfter(caret);

sel.removeAllRanges();
sel.addRange(range);

/**
* A little timeout uses to allow browser to set caret after element before we remove it.
*/
setTimeout(() => {
caret.remove();
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
}, 150);
};
}
1 change: 0 additions & 1 deletion packages/caret/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
Expand Down
40 changes: 40 additions & 0 deletions packages/dom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# @editorjs/dom
Utils useful for work with dom for Editor.js tools development
### Installation
```
npm install @editorjs/dom
```
### Function list
- [allInputsSelector](https://github.com/editor-js/utils/blob/main/packages/dom/src/allInputsSelector.ts) - Returns CSS selector for all text inputs
- [append](https://github.com/editor-js/utils/blob/main/packages/dom/src/append.ts) - Append one or several elements to the parent
- [blockElements](https://github.com/editor-js/utils/blob/main/packages/dom/src/blockElements.ts) - Return array of names of block html elements
- [calculateBaseline](https://github.com/editor-js/utils/blob/main/packages/dom/src/calculateBaseline.ts) - Calculates the Y coordinate of the text baseline from the top of the element's margin box,
- [canSetCaret](https://github.com/editor-js/utils/blob/main/packages/dom/src/canSetCaret.ts) - Checks if we can set caret
- [containsOnlyInlineElements](https://github.com/editor-js/utils/blob/main/packages/dom/src/containsOnlyInlineElements.ts) - Check if passed content includes only inline elements
- [findAllInputs](https://github.com/editor-js/utils/blob/main/packages/dom/src/findAllInputs.ts) - Find all contenteditable, textarea and editable input elements passed holder contains
- [fragmentToString](https://github.com/editor-js/utils/blob/main/packages/dom/src/fragmentToString.ts) - Returns the HTML content of passed Document Fragment
- [getContentLength](https://github.com/editor-js/utils/blob/main/packages/dom/src/getContentLength.ts) - Return length of node`s text content
- [getDeepestBlockElements](https://github.com/editor-js/utils/blob/main/packages/dom/src/getDeepestBlockElements.ts) - Find and return all block elements in the passed parent (including subtree)
- [getDeepestNode](https://github.com/editor-js/utils/blob/main/packages/dom/src/getDeepestNode.ts) - Search for deepest node which is Leaf.
- [isCollapsedWhitespaces](https://github.com/editor-js/utils/blob/main/packages/dom/src/isCollapsedWhitespaces.ts) - Determine whether a passed text content is a collapsed whitespace.
- [isContentEditable](https://github.com/editor-js/utils/blob/main/packages/dom/src/isContentEditable.ts) - Check if passed element is contenteditable
- [isElement](https://github.com/editor-js/utils/blob/main/packages/dom/src/isElement.ts) - Check if object is DOM node
- [isEmpty](https://github.com/editor-js/utils/blob/main/packages/dom/src/isEmpty.ts) - breadth-first search (BFS)
- [isFragment](https://github.com/editor-js/utils/blob/main/packages/dom/src/isFragment.ts) - Check if object is DocumentFragment node
- [isHTMLString](https://github.com/editor-js/utils/blob/main/packages/dom/src/isHtmlString.ts) - Check if string contains html elements
- [isLeaf](https://github.com/editor-js/utils/blob/main/packages/dom/src/isLeaf.ts) - checks node if it is doesn't have any child nodes
- [isLineBreakTag](https://github.com/editor-js/utils/blob/main/packages/dom/src/isLineBreakTag.ts) - Check if element is BR or WBR
- [isNativeInput](https://github.com/editor-js/utils/blob/main/packages/dom/src/isNativeInput.ts) - Checks target if it is native input
- [isNodeEmpty](https://github.com/editor-js/utils/blob/main/packages/dom/src/isNodeEmpty.ts) - Checks node if it is empty
- [isSingleTag](https://github.com/editor-js/utils/blob/main/packages/dom/src/isSingleTag.ts) - Check if passed tag has no closed tag
- [make](https://github.com/editor-js/utils/blob/main/packages/dom/src/make.ts) - Helper for making Elements with class name and attributes
- [offset](https://github.com/editor-js/utils/blob/main/packages/dom/src/offset.ts) - Return element's offset related to the document
- [prepend](https://github.com/editor-js/utils/blob/main/packages/dom/src/prepend.ts) - Append element or a couple to the beginning of the parent elements
# About CodeX
<img align="right" width="120" height="120" src="https://codex.so/public/app/img/codex-logo.svg" hspace="50">

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/) |
3 changes: 2 additions & 1 deletion packages/dom/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@editorjs/dom",
"version": "0.0.1",
"description": "Utils useful for work with dom for Editor.js tools development",
"version": "0.0.2",
"main": "dist/index.js",
"license": "MIT",
"scripts": {
Expand Down
9 changes: 9 additions & 0 deletions packages/dom/src/allInputsSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Returns CSS selector for all text inputs
*/
export function allInputsSelector(): string {
const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url'];

return '[contenteditable=true], textarea, input:not([type]), '
+ allowedInputTypes.map(type => `input[type="${type}"]`).join(', ');
}
2 changes: 1 addition & 1 deletion packages/dom/src/canSetCaret.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNativeInput } from './inputs';
import { isNativeInput } from './isNativeInput';
import { isContentEditable } from './isContentEditable';

/**
Expand Down
24 changes: 24 additions & 0 deletions packages/dom/src/findAllInputs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { containsOnlyInlineElements } from './containsOnlyInlineElements';
import { getDeepestBlockElements } from './getDeepestBlockElements';
import { allInputsSelector } from './allInputsSelector';
import { isNativeInput } from './isNativeInput';

/**
* Find all contenteditable, textarea and editable input elements passed holder contains
* @param holder - element where to find inputs
* @returns - all inputs of the holder element
*/
export function findAllInputs(holder: HTMLElement): HTMLElement[] {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return Array.from(holder.querySelectorAll(allInputsSelector()) as NodeListOf<HTMLElement>)
/**
* If contenteditable element contains block elements, treat them as inputs.
*/
.reduce((result, input) => {
if (isNativeInput(input) || containsOnlyInlineElements(input)) {
return [...result, input];
}

return [...result, ...getDeepestBlockElements(input)] as HTMLElement[];
}, []);
}
14 changes: 14 additions & 0 deletions packages/dom/src/fragmentToString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { make } from './make';

/**
* Returns the HTML content of passed Document Fragment
* @param fragment - document fragment to process
* @returns the HTML content of passed Document Fragment
*/
export function fragmentToString(fragment: DocumentFragment): string {
const div = make('div');

div.appendChild(fragment);

return div.innerHTML;
}
2 changes: 1 addition & 1 deletion packages/dom/src/getContentLength.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNativeInput } from './inputs';
import { isNativeInput } from './isNativeInput';

/**
* Return length of node`s text content
Expand Down
5 changes: 3 additions & 2 deletions packages/dom/src/getDeepestNode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isNativeInput } from './inputs';
import { isLineBreakTag, isSingleTag } from './isTag';
import { isNativeInput } from './isNativeInput';
import { isLineBreakTag } from './isLineBreakTag';
import { isSingleTag } from './isSingleTag';

/**
* Search for deepest node which is Leaf.
Expand Down
Loading
Loading