Skip to content

Commit

Permalink
Create API methods for manipulating with block elements (#135)
Browse files Browse the repository at this point in the history
* create API methods for manipulating with block elements
* get Element implemnted
* finish API of elements
* add new API object Elements and Blocks
* remove elements API from editor.blocks

* Publish

 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]
 - @yoopta/[email protected]

* add carousel block options
  • Loading branch information
Darginec05 authored Jun 5, 2024
1 parent 4c15abd commit e655c06
Show file tree
Hide file tree
Showing 134 changed files with 3,736 additions and 1,755 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
],
"private": true,
"scripts": {
"start": "yarn lerna run start --scope @yoopta/editor --scope @yoopta/paragraph --parallel --ignore development",
"start": "yarn lerna run start --parallel --ignore development",
"build": "yarn clean && yarn lerna run build --parallel --ignore development",
"clean": "find ./packages -type d -name dist ! -path './packages/development/*' -exec rm -rf {} +",
"serve": "yarn lerna run dev --scope=development",
Expand Down
5 changes: 3 additions & 2 deletions packages/core/editor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yoopta/editor",
"version": "4.4.1",
"version": "4.5.0",
"license": "MIT",
"private": false,
"main": "dist/index.js",
Expand Down Expand Up @@ -66,5 +66,6 @@
"bugs": {
"url": "https://github.com/Darginec05/Yoopta-Editor/issues"
},
"homepage": "https://github.com/Darginec05/Yoopta-Editor#readme"
"homepage": "https://github.com/Darginec05/Yoopta-Editor#readme",
"gitHead": "29e4ae316ec75bb43d3822d028abcb0c34256ec5"
}
3 changes: 2 additions & 1 deletion packages/core/editor/src/UI/BlockOptions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export function buildActionMenuRenderProps({ editor, view, onClose, mode = 'togg
return items.map((action) => {
const title = editor.blocks[action].options?.display?.title || action;
const description = editor.blocks[action].options?.display?.description;
return { type: action, title, description };
const icon = editor.blocks[action].options?.display?.icon;
return { type: action, title, description, icon };
});
};

Expand Down
1 change: 1 addition & 0 deletions packages/core/editor/src/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ const Editor = ({ placeholder, marks, className, selectionBoxRoot, width, childr
resetSelectedBlocks();
};

// [TODO] - implement with @yoopta/exports
const onCopy = (event: React.ClipboardEvent) => {
// function escapeHtml(text) {
// const map = {
Expand Down
20 changes: 11 additions & 9 deletions packages/core/editor/src/components/Editor/utils.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { YooptaBlockData, SlateElement } from '../../editor/types';
import { generateId } from '../../utils/generateId';

export const buildBlockElement = (element?: Partial<SlateElement>): SlateElement => ({
id: generateId(),
type: element?.type || 'paragraph',
children: element?.children || [{ text: '' }],
props: {
nodeType: 'block',
...element?.props,
},
});
export const buildBlockElement = (element?: Partial<SlateElement>): SlateElement => {
return {
id: generateId(),
type: element?.type || 'paragraph',
children: element?.children || [{ text: '' }],
props: {
nodeType: 'block',
...element?.props,
},
};
};

export const buildBlockData = (block?: Partial<YooptaBlockData>): YooptaBlockData => ({
id: block?.id || generateId(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { createDraft, finishDraft } from 'immer';
import { Element, Transforms } from 'slate';
import { buildBlockData, buildBlockElement } from '../../components/Editor/utils';
import { getRootBlockElementType } from '../../utils/blockElements';
import { Transforms } from 'slate';
import { buildBlockData } from '../../components/Editor/utils';
import { buildBlockElementsStructure } from '../../utils/blockElements';
import { findPluginBlockBySelectionPath } from '../../utils/findPluginBlockBySelectionPath';
import { findSlateBySelectionPath } from '../../utils/findSlateBySelectionPath';
import { generateId } from '../../utils/generateId';
import { YooEditor, YooptaEditorTransformOptions } from '../types';
import { YooEditor, YooptaEditorTransformOptions, SlateElement, YooptaBlock } from '../types';

export type CreateBlockOptions = YooptaEditorTransformOptions & {
deleteText?: boolean;
Expand All @@ -21,23 +20,15 @@ export function createBlock(editor: YooEditor, type: string, options?: CreateBlo
if (!slate || !slate.selection) return;

const selectedBlock = editor.blocks[type];
const rootBlockElementType = getRootBlockElementType(selectedBlock.elements);
const rootBlockElement = selectedBlock.elements[rootBlockElementType!];
const elements = buildBlockElementsStructure(editor, type);

if (!rootBlockElement) return;
// Transforms.setNodes(slate, elements, {
// at: [0],
// match: (n) => Element.isElement(n),
// mode: 'highest',
// });

const nodeProps = { nodeType: rootBlockElement.props?.nodeType || 'block', ...rootBlockElement.props };
const elementNode = buildBlockElement({
id: generateId(),
type: rootBlockElementType,
props: nodeProps,
});

Transforms.setNodes(slate, elementNode, {
at: [0, 0],
match: (n) => Element.isElement(n),
mode: 'highest',
});
// Transforms.insertNodes(slate, elements, { at: slate.selection.anchor.path.slice(0, 1) });

if (options?.deleteText) Transforms.delete(slate, { at: [0, 0] });

Expand All @@ -48,9 +39,10 @@ export function createBlock(editor: YooEditor, type: string, options?: CreateBlo
order: block.meta.order,
depth: block.meta.depth,
},
value: [elementNode],
value: [elements],
});

slate.children = blockData.value;
const blockId = blockData.id;

editor.children[blockId] = blockData;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { createDraft, finishDraft } from 'immer';
import { createEditor, Editor } from 'slate';
import { withHistory } from 'slate-history';
import { withReact } from 'slate-react';
import { buildBlockData } from '../../components/Editor/utils';
import { buildSlateEditor } from '../../utils/editorBuilders';
import { withShortcuts } from '../../extensions/shortcuts';
import { findPluginBlockBySelectionPath } from '../../utils/findPluginBlockBySelectionPath';
import { generateId } from '../../utils/generateId';
import { YooEditor, YooptaEditorTransformOptions } from '../types';
// // [TODO] Circular deps
// import { buildSlateEditor } from '../../utils/editorBuilders';

function buildSlateEditor(editor: YooEditor): Editor {
const slate = withShortcuts(editor, withHistory(withReact(createEditor())));
return slate;
}

export type DeleteBlockOptions = YooptaEditorTransformOptions & {
deleteAll?: boolean;
Expand Down
27 changes: 27 additions & 0 deletions packages/core/editor/src/editor/blocks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { insertBlock } from './insertBlock';
import { deleteBlock } from './deleteBlock';
import { moveBlock } from './moveBlock';
import { focusBlock } from './focusBlock';
import { splitBlock } from './splitBlock';
import { increaseBlockDepth } from './increaseBlockDepth';
import { decreaseBlockDepth } from './decreaseBlockDepth';
import { duplicateBlock } from './duplicateBlock';
import { updateBlock } from './updateBlock';
import { toggleBlock } from './toggleBlock';
import { insertBlocks } from './insertBlocks';

export const Blocks = {
insertBlock,
deleteBlock,
moveBlock,
focusBlock,
splitBlock,
increaseBlockDepth,
decreaseBlockDepth,
duplicateBlock,
updateBlock,
toggleBlock,
insertBlocks,
// [TODO]
// updateBlocks
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getRootBlockElementType } from '../../utils/blockElements';

import { findPluginBlockBySelectionPath } from '../../utils/findPluginBlockBySelectionPath';
import { findSlateBySelectionPath } from '../../utils/findSlateBySelectionPath';
import { YooEditor, YooptaBlockPath, YooptaEditorTransformOptions, YooptaBlockData } from '../types';
import { YooEditor, YooptaEditorTransformOptions, YooptaBlockData } from '../types';

export type ToggleBlockOptions = YooptaEditorTransformOptions & {
deleteText?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ export function updateBlock<TElementKeys extends string, TProps>(
let shouldApply = false;

const block = editor.children[blockId];

if (!block) {
throw Error(`Block with id ${blockId} not found`);
// [TODO] - some weird behaviour when copy/paste
console.log(`Block with id ${blockId} not found`);
return;
}

if (data.id) {
Expand Down
96 changes: 96 additions & 0 deletions packages/core/editor/src/editor/elements/createElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Editor, Path, Span, Transforms } from 'slate';
import { buildBlockElement } from '../../components/Editor/utils';
import { findSlateBySelectionPath } from '../../utils/findSlateBySelectionPath';
import { SlateElement, YooEditor } from '../types';
import { getElementEntry } from './getElementEntry';

export type CreateBlockElementOptions = {
path?: 'next' | 'prev' | Path | Span;
focus?: boolean;
split?: boolean;
};

export type CreateElement<TElementKeys, TElementProps> = {
type: TElementKeys;
props?: TElementProps;
};

export function createElement<TElementKeys extends string, TElementProps>(
editor: YooEditor,
blockId: string,
element: CreateElement<TElementKeys, TElementProps>,
options?: CreateBlockElementOptions,
) {
const blockData = editor.children[blockId];
if (!blockData) {
throw new Error(`Block with id ${blockId} not found`);
}

const slate = findSlateBySelectionPath(editor, { at: [blockData.meta.order] });
if (!slate) {
console.warn('No slate found');
return;
}

Editor.withoutNormalizing(slate, () => {
const block = editor.blocks[blockData.type];
const blockElement = block.elements[element.type];
const nodeElement = buildBlockElement({ type: element.type, props: { ...blockElement.props, ...element.props } });

const elementTypes = Object.keys(block.elements);

let childrenElements: SlateElement[] = [];

elementTypes.forEach((blockElementType) => {
const blockElement = block.elements[blockElementType];

if (blockElementType === element.type) {
if (Array.isArray(blockElement.children) && blockElement.children.length > 0) {
blockElement.children.forEach((childElementType) => {
const childElement = block.elements[childElementType];
childrenElements.push(buildBlockElement({ type: childElementType, props: childElement.props }));
});
}
}
});

if (childrenElements.length > 0) nodeElement.children = childrenElements;

const { path, focus = true } = options || {};
let atPath;

const elementEntry = getElementEntry(editor, blockId, { type: element.type });

if (elementEntry) {
const [, elementPath] = elementEntry;

if (Path.isPath(path)) {
atPath = path;
} else if (path === 'prev') {
atPath = Path.previous(elementPath);
} else if (path === 'next') {
atPath = Path.next(elementPath);
}
}

Transforms.insertNodes(slate, nodeElement, { at: atPath, select: focus });

if (focus) {
if (childrenElements.length > 0) {
const firstChild = childrenElements[0];
const firstElementEntry = getElementEntry(editor, blockId, {
path: atPath,
type: firstChild.type,
});

if (firstElementEntry) {
const [, firstElementPath] = firstElementEntry;
Transforms.select(slate, firstElementPath);
}
}
}

editor.applyChanges();
editor.emit('change', editor.children);
});
}
33 changes: 33 additions & 0 deletions packages/core/editor/src/editor/elements/deleteElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Editor, Element, Path, Transforms } from 'slate';
import { findSlateBySelectionPath } from '../../utils/findSlateBySelectionPath';
import { YooEditor } from '../types';

export type DeleteBlockElement = {
type: string;
path: Path;
};

export function deleteElement(editor: YooEditor, blockId: string, element: DeleteBlockElement) {
const block = editor.children[blockId];

if (!block) {
throw new Error(`Block with id ${blockId} not found`);
}

const slate = findSlateBySelectionPath(editor, { at: [block.meta.order] });

if (!slate) {
console.warn('No slate found');
return;
}

Editor.withoutNormalizing(slate, () => {
Transforms.removeNodes(slate, {
at: element.path,
match: (n) => Element.isElement(n) && n.type === element.type,
});

editor.applyChanges();
editor.emit('change', editor.children);
});
}
18 changes: 18 additions & 0 deletions packages/core/editor/src/editor/elements/getElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { SlateElement, YooEditor } from '../types';
import { getElementEntry, GetBlockElementEntryOptions } from './getElementEntry';

export type GetBlockElementOptions = GetBlockElementEntryOptions;

export function getElement<TElementKeys extends string>(
editor: YooEditor,
blockId: string,
options?: GetBlockElementOptions,
): SlateElement<TElementKeys> | undefined {
const elementEntry = getElementEntry(editor, blockId, options);

if (elementEntry) {
return elementEntry[0] as SlateElement<TElementKeys>;
}

return undefined;
}
16 changes: 16 additions & 0 deletions packages/core/editor/src/editor/elements/getElementChildren.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { SlateElement, YooEditor } from '../types';
import { getElement } from './getElement';
import { GetBlockElementEntryOptions } from './getElementEntry';

export type GetElementChildrenOptions = GetBlockElementEntryOptions;

export function getElementChildren<TElementKeys extends string>(
editor: YooEditor,
blockId: string,
options?: GetElementChildrenOptions,
): SlateElement<TElementKeys>['children'] | undefined {
const element = getElement(editor, blockId, options);
if (element) return element.children;

return undefined;
}
Loading

0 comments on commit e655c06

Please sign in to comment.