Skip to content

Added refElement to editor instance #219

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

Merged
merged 4 commits into from
Jul 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading