Skip to content

Commit

Permalink
Added refElement to editor instance (#219)
Browse files Browse the repository at this point in the history
* added `refElement` to editor instance
  • Loading branch information
Darginec05 authored Jul 21, 2024
1 parent bf82748 commit a96b9c4
Show file tree
Hide file tree
Showing 19 changed files with 87 additions and 369 deletions.
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,16 @@ All of this is customizable, extensible, and easy to set up!

- [**@yoopta/paragraph**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/paragraph/README.md)
- [**@yoopta/accordion**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/accordion/README.md)
- **@yoopta/blockquote**
- **@yoopta/code**
- **@yoopta/embed**
- **@yoopta/image**
- **@yoopta/link**
- **@yoopta/file**
- **@yoopta/callout**
- **@yoopta/video**
- [**@yoopta/blockquote**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/blockquote/README.md)
- [**@yoopta/code**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/code/README.md)
- [**@yoopta/embed**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/embed/README.md)
- [**@yoopta/image**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/image/README.md)
- [**@yoopta/link**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/link/README.md)
- [**@yoopta/file**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/file/README.md)
- [**@yoopta/callout**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/callout/README.md)
- [**@yoopta/video**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/video/README.md)
- [**@yoopta/lists**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/lists/README.md)
- **@yoopta/headings**
- [**@yoopta/headings**](https://github.com/Darginec05/Yoopta-Editor/blob/master/packages/plugins/headings/README.md)

- Tools

Expand Down Expand Up @@ -288,16 +288,16 @@ Find below useful examples of utilising the Yoopta-Editor in your projects. Thes

Okay, let's go!

- [With basic example](https://yoopta-editor.vercel.app/examples/withBaseFullSetup)
- [With custom toolbar (Notion and Medium example)](https://yoopta-editor.vercel.app/examples/withCustomToolbar)
- [With Notion Action Menu](https://yoopta-editor.vercel.app/examples/withNotionActionMenu)
- [With dark theme](https://yoopta-editor.vercel.app/examples/withDarkTheme)
- [With media plugins](https://yoopta-editor.vercel.app/examples/withMedia)
- [With extended plugins](https://yoopta-editor.vercel.app/examples/withExtendedPlugin)
- [With readonly](https://yoopta-editor.vercel.app/examples/withReadOnly)
- [With custom HTML attributes](https://yoopta-editor.vercel.app/examples/withCustomHTMLAttributes)
- [With custom mark](https://yoopta-editor.vercel.app/examples/withCustomMark)
- [With chat slack](https://yoopta-editor.vercel.app/examples/withChatSlack)
- [With basic example](https://yoopta.dev/examples/withBaseFullSetup)
- [With custom toolbar (Notion and Medium example)](https://yoopta.dev/examples/withCustomToolbar)
- [With Notion Action Menu](https://yoopta.dev/examples/withNotionActionMenu)
- [With dark theme](https://yoopta.dev/examples/withDarkTheme)
- [With media plugins](https://yoopta.dev/examples/withMedia)
- [With extended plugins](https://yoopta.dev/examples/withExtendedPlugin)
- [With readonly](https://yoopta.dev/examples/withReadOnly)
- [With custom HTML attributes](https://yoopta.dev/examples/withCustomHTMLAttributes)
- [With custom mark](https://yoopta.dev/examples/withCustomMark)
- [With chat slack](https://yoopta.dev/examples/withChatSlack)
- ...and check other examples in the sidebar list

## Give us ⭐️ star
Expand Down
158 changes: 15 additions & 143 deletions packages/core/editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,185 +73,57 @@ type Props = {
};
```

### API from editor instance
### Editor API

```tsx
type YooEditor<TNodes> = {
/**
* Unique identifier for the editor instance.
*/
export type YooEditor<TNodes> = {
id: string;

/**
* Inserts a single block of data into the editor.
*/
readOnly: boolean;
insertBlock: (data: YooptaBlockData, options?: YooptaEditorTransformOptions) => void;

/**
* Inserts multiple blocks of data into the editor at once.
*/
insertBlocks: (blocks: YooptaBlockData[], options?: YooptaEditorTransformOptions) => void;

/**
* Splits the current block at the cursor's position.
*/
splitBlock: (options?: YooptaEditorTransformOptions) => void;

/**
* Updates a block with new data based on its id.
*/
updateBlock: (id: string, data: Partial<YooptaBlockData>) => void;

/**
* Deletes block from the editor children.
*/
deleteBlock: (options?: DeleteBlockOptions) => void;

/**
* Deletes blocks from paths or blockIds
*
*/
deleteBlocks: (options?: DeleteBlocksOptions) => void;

/**
* Duplicates a block within the editor.
*/
duplicateBlock: (options?: DuplicateBlockOptions) => void;

/**
* Retrieves a block's data based on provided options.
*/
getBlock: (options?: YooptaEditorTransformOptions) => void;

/**
* Toggles a block's type in the editor.
*/
toggleBlock: (toBlockType?: string, options?: ToggleBlockOptions) => void;

/**
* Increases the depth of a block, typically used for nested structures like lists.
*/
toggleBlock: (toBlockType: string, options?: ToggleBlockOptions) => void;
increaseBlockDepth: (options?: YooptaEditorTransformOptions) => void;

/**
* Decreases the depth of a block.
*/
decreaseBlockDepth: (options?: YooptaEditorTransformOptions) => void;

/**
* Applies any staged changes to the editor.
*/
applyChanges: () => void;

/**
* Moves a block to a different position within the editor.
*/
moveBlock: (blockId: string, to: YooptaBlockPath) => void;

/**
* Focuses on a block based on its id.
*/
focusBlock: (id: string, options?: FocusBlockOptions) => void;

/**
* Current selection path in the editor.
*/
getBlock: (options: GetBlockOptions) => YooptaBlockData | null;
selection: YooptaBlockPath | null;

/**
* Array of currently selected block indices.
*/
selectedBlocks: number[] | null;

/**
* Holds the structured data representing the editor's content.
*/
children: Record<string, YooptaBlockData>;

/**
* Retrieves the entire editor's value.
*/
getEditorValue: () => TNodes;

/**
* Sets the editor's content.
*/
setEditorValue: (value: YooptaContentValue) => void;

/**
* Sets the selection path in the editor.
*/
setSelection: (path: YooptaBlockPath | null, options?: SetSelectionOptions) => void;

/**
* Sets or clears the selection for a block.
*/
setBlockSelected: (path: number[] | null, options?: BlockSelectedOptions) => void;

/**
* Map of individual block editors.
*/
blockEditorsMap: YooptaPluginsEditorMap;

/**
* Definitions of all blocks in the editor.
*/
blocks: YooptaBlocks;

/**
* Formatting options available in the editor.
*/
formats: YooptaFormats;

/**
* Keyboard shortcuts configured for the editor.
*/
shortcuts: Record<string, YooptaBlock>;

/**
* Registered plugins.
*/
plugins: Record<string, Plugin<string, unknown>>;

/**
* Subscribes to an event.
*/
// events handlers
on: (event: YooEditorEvents, fn: (payload: any) => void) => void;

/**
* Subscribes once to an event.
*/
once: (event: YooEditorEvents, fn: (payload: any) => void) => void;

/**
* Unsubscribes from an event.
*/
off: (event: YooEditorEvents, fn: (payload: any) => void) => void;

/**
* Emits an event.
*/
emit: (event: YooEditorEvents, payload: any) => void;

/**
* Indicates if the editor is read-only.
*/
readOnly: boolean;

/**
* Returns whether the editor is focused.
*/
// focus handlers
isFocused: () => boolean;

/**
* Blurs the editor, removing focus.
*/
blur: (options?: EditorBlurOptions) => void;

/**
* Focuses the editor.
*/
focus: () => void;

// parser handlers
getHTML: (content: YooptaContentValue) => string;
getMarkdown: (content: YooptaContentValue) => string;
getPlainText: (content: YooptaContentValue) => string;

// ref to editor element
refElement: HTMLElement | null;
};
```

Expand Down
7 changes: 5 additions & 2 deletions packages/core/editor/src/UI/Portal/Portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const Portal = (props: Props) => {

useEffect(() => {
setIsMounted(true);
const editorEl = document.querySelector(`[data-yoopta-editor-id="${editor.id}"]`) as HTMLElement;
const editorEl = editor.refElement;

if (!editorEl) return;

const overlays = editorEl.querySelector('.yoopta-overlays');
if (!overlays) {
rootEl.current = document.createElement('div');
Expand All @@ -32,7 +35,7 @@ const Portal = (props: Props) => {
if (!isMounted) return null;

return (
<FloatingPortal id={`${props.id}-${editor.id}`} root={rootEl.current}>
<FloatingPortal id={`${props.id}-${editor.id}`} root={rootEl.current || editor.refElement}>
{props.children}
</FloatingPortal>
);
Expand Down
16 changes: 7 additions & 9 deletions packages/core/editor/src/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ const Editor = ({
}: Props) => {
const editor = useYooptaEditor();
const isReadOnly = useYooptaReadOnly();
const yooptaEditorRef = useRef<HTMLDivElement>(null);
const selectionBox = useRectangeSelectionBox({ editor, yooptaEditorRef, root: selectionBoxRoot });
const selectionBox = useRectangeSelectionBox({ editor, root: selectionBoxRoot });

let state = useRef<State>(DEFAULT_STATE).current;

Expand All @@ -73,11 +72,11 @@ const Editor = ({
}, [editor.selectedBlocks, isReadOnly]);

const handleEmptyZoneClick = (e: React.MouseEvent) => {
const editorRef = yooptaEditorRef.current;
if (!editorRef) return;
const editorEl = editor.refElement;
if (!editorEl) return;

const { bottom } = editorRef.getBoundingClientRect();
const paddingBottom = parseInt(getComputedStyle(editorRef).paddingBottom, 10);
const { bottom } = editorEl.getBoundingClientRect();
const paddingBottom = parseInt(getComputedStyle(editorEl).paddingBottom, 10);
const paddingBottomAreaTop = bottom - paddingBottom;
const defaultBlock = buildBlockData({ id: generateId() });

Expand Down Expand Up @@ -151,7 +150,7 @@ const Editor = ({
};

const onBlur = (event: React.FocusEvent) => {
const isInsideEditor = yooptaEditorRef.current?.contains(event.relatedTarget as Node);
const isInsideEditor = editor.refElement?.contains(event.relatedTarget as Node);
if (isInsideEditor || isReadOnly) return;

resetSelectionState();
Expand Down Expand Up @@ -392,10 +391,9 @@ const Editor = ({

return (
<div
data-yoopta-editor-id={editor.id}
ref={(ref) => (editor.refElement = ref)}
className={className ? `yoopta-editor ${className}` : 'yoopta-editor'}
style={editorStyles}
ref={yooptaEditorRef}
onMouseDown={onMouseDown}
onBlur={onBlur}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { YooEditor } from '../../editor/types';

export type RectangeSelectionProps = {
editor: YooEditor;
yooptaEditorRef: React.RefObject<HTMLDivElement>;
root?: HTMLElement | React.MutableRefObject<HTMLElement | null> | false;
};

Expand Down
18 changes: 8 additions & 10 deletions packages/core/editor/src/components/SelectionBox/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useEffect, useState } from 'react';
import { YooEditor } from '../../editor/types';
import { RectangeSelectionProps, RectangeSelectionState } from './SelectionBox';

const findBlocksUnderSelection = (editorId, origin, coords) => {
const findBlocksUnderSelection = (editor: YooEditor, origin, coords) => {
const blocksUnderSelection: number[] = [];
const blocks = editor.refElement?.querySelectorAll(`[data-yoopta-block]`);

const blocks = document.querySelectorAll(`[data-yoopta-editor-id="${editorId}"] [data-yoopta-block]`);
if (!blocks) return blocksUnderSelection;

blocks.forEach((blockEl, i) => {
if (!blockEl) return;
Expand Down Expand Up @@ -36,11 +38,7 @@ type RectangeSelectionReturn = RectangeSelectionState & {

// [TODO] - Fix selection when multiple editors
// Maybe move to a separate npm package?
export const useRectangeSelectionBox = ({
editor,
yooptaEditorRef,
root,
}: RectangeSelectionProps): RectangeSelectionReturn => {
export const useRectangeSelectionBox = ({ editor, root }: RectangeSelectionProps): RectangeSelectionReturn => {
const [state, setState] = useState<RectangeSelectionState>({
origin: [0, 0],
coords: [0, 0],
Expand All @@ -50,7 +48,7 @@ export const useRectangeSelectionBox = ({
const onMouseDown = (event) => {
if (editor.readOnly || root === false) return;

const isInsideEditor = yooptaEditorRef.current?.contains(event.target as Node);
const isInsideEditor = editor.refElement?.contains(event.target as Node);

if (
!isInsideEditor &&
Expand Down Expand Up @@ -86,7 +84,7 @@ export const useRectangeSelectionBox = ({
coords: [event.pageX, event.pageY - window.pageYOffset],
}));

const blocksUnderSelection = findBlocksUnderSelection(editor.id, state.origin, [
const blocksUnderSelection = findBlocksUnderSelection(editor, state.origin, [
event.pageX,
event.pageY - window.pageYOffset,
]);
Expand Down Expand Up @@ -119,7 +117,7 @@ export const useRectangeSelectionBox = ({
throw new Error('Root element should be a DOM element or a ref object. Please check the `selectionBoxRoot` prop');
}

if (yooptaEditorRef.current?.contains(elementMouseEl)) {
if (editor.refElement?.contains(elementMouseEl)) {
throw new Error('Root element should not be a child of the editor. Please check the `selectionBoxRoot` prop');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const DEFAULT_HANDLERS: YooptaEditorContext = {
getHTML: () => '',
getMarkdown: () => '',
getPlainText: () => '',

refElement: null,
},
};

Expand Down
Loading

0 comments on commit a96b9c4

Please sign in to comment.