Skip to content

Commit

Permalink
Merge pull request #3597 from udecode/perf/opti
Browse files Browse the repository at this point in the history
Optimize block selection & draggable
  • Loading branch information
felixfeng33 authored Oct 1, 2024
2 parents 8070409 + 0df9808 commit 2c179e8
Show file tree
Hide file tree
Showing 99 changed files with 1,222 additions and 712 deletions.
5 changes: 5 additions & 0 deletions .changeset/core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@udecode/plate-core': patch
---

`useOptions`, `useOption` missing plugins now warn instead of erroring.
9 changes: 9 additions & 0 deletions .changeset/dnd copy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@udecode/plate-dnd': minor
---

- New hooks:
- `useDraggableGutter`: Returns props for the draggable gutter.
- `useDropLine`: Returns the current drop line state and props.
- Added `DraggableProvider` and `useDraggableStore` for managing draggable state:
- `dropLine`: The direction of the drop line.
10 changes: 10 additions & 0 deletions .changeset/dnd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@udecode/plate-dnd': major
---

The following changes were made to improve performance:

- Refactored `useDraggable` hook to focus on core dragging functionality:
- Removed `dropLine`. Use `useDropLine().dropLine` instead.
- Removed `groupProps` from the returned object – `isHovered`, and `setIsHovered` from the returned state. Use CSS instead.
- Removed `droplineProps`, and `gutterLeftProps` from the returned object. Use `useDropLine().props`, `useDraggableGutter().props` instead.
7 changes: 7 additions & 0 deletions .changeset/selection copy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@udecode/plate-selection': minor
---

- `BlockSelectableProvider`, `useBlockSelectableStore` to store `selectable` state.
- Introduced `isSelectionAreaVisible` option in `BlockSelectionPlugin` config. Useful to hide some UI when `true`.
- New `useBlockSelected` hook for checking if a block is selected.
9 changes: 9 additions & 0 deletions .changeset/selection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@udecode/plate-selection': major
---

The following changes were made to improve performance:

- Removed `useHooksBlockSelection` in favor of `BlockSelectionAfterEditable`
- Removed `slate-selected` class from `BlockSelectable`. You can do it on your components using `useBlockSelected()` instead, or by using our new `block-selection.tsx` component.
- Introduced `useBlockSelectableStore` for managing selectable state.
5 changes: 5 additions & 0 deletions .changeset/wild-planets-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@udecode/react-utils': patch
---

New component: MemoizedChildren
72 changes: 46 additions & 26 deletions apps/www/content/docs/block-selection.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ description: Select and manipulate entire text blocks.

<PackageInfo>

The Block Selection feature allows users to select and manipulate entire text blocks, as opposed to individual words or characters. This powerful functionality enhances the editing experience by providing efficient ways to manage large sections of content.

## Features

- Select entire blocks, as opposed to individual words or characters.
- To select an entire block, mouse down outside the text area and then move the cursor into the block. Once it is selected, you'll see a background color.
- Keep moving down or up to select multiple blocks.
- Once selected, the available actions are: copy, cut, and delete.
- Keyboard shortcuts:
- `Cmd+A` (Mac) / `Ctrl+A` (Windows/Linux):
- First press: Selects the current block
- Double press: Selects the whole document using block selection
- Note: This behavior can be disabled by setting `handlers.onKeyDown = null` when creating the plugin
- Select entire blocks with a single action
- Multi-block selection
- Copy, cut, and delete operations on selected blocks
- Keyboard shortcuts for quick selection:
- `Cmd+A`:
- First press: select the current block
- Double press: select all blocks
- Arrow keys: select the block above or below
- Customizable styling for selected blocks

</PackageInfo>

Expand All @@ -40,7 +42,9 @@ const plugins = [
];
```

## Set scrollable container
## Configuration

### Set scrollable container

To control the scrollable container, configure the `boundaries` and `container` options within `areaOptions`. These options accept CSS selectors, such as `#selection-demo #scroll_container`, which are used with `document.querySelector()`.

Expand All @@ -64,7 +68,7 @@ Example configuration:

This setup ensures that the block selection functionality is properly constrained within your designated scrollable area.

## Set selectable element
### Set selectable element

Add data-plate-selectable to the container or the element you want to start block selection.

Expand All @@ -89,36 +93,48 @@ Example:
/>
```

### Styling
## Styling

### Selection area

#### Selection area
You can style the selection area by adding this class to the container(.slate-selection-area can be changed in the plugin options):
Style the selection area using `.slate-selection-area` class. For example:

```js
'[&_.slate-selection-area]:border [&_.slate-selection-area]:border-primary [&_.slate-selection-area]:bg-primary/10'
```

#### Selected element
### Selected element

You can style the selected element by adding this class to the container
To determine if an element is selected, use the new `useBlockSelected` hook. You can render a visual indicator around selected blocks using our `BlockSelection` component or create your own:

```js
'[&_.slate-selected]:!bg-primary/20 '
```
```tsx
import React from 'react';
import { useBlockSelected } from '@udecode/plate-selection/react';

### Disable browser default scroll behavior
If we select some text and then move(keep pressed) the cursor to the very bottom of the page, the page will start scrolling to the bottom.
export function BlockSelection() {
const isBlockSelected = useBlockSelected();

Sometimes this scroll is so fast and confilicts with `BlockSelectionPlugin` scroll behavior(After the selection area appears, move the mouse to the bottom of the scroll container).
return (
<div
className={`pointer-events-none absolute inset-0 z-10 bg-primary/15 ${
isBlockSelected ? 'opacity-100' : 'opacity-0'
}`}
/>
);
}
```

I don't find any way to disable this scroll behavior of browser.(if you find the way to disable it, please tell me that will so appreciate it)
This component should be rendered inside each block element for consistent selection feedback. Plate UI is doing it in `plate-element.tsx`.

So the solution is add an empty `div` above the editer's right side(The right side is the easiest place to trigger this behavior.) and set `user-select: none` to it .
### Disable browser default scroll behavior

This will make configuration and maintenance cumbersome so we remove this div from plate.
When selecting text and moving the cursor to the bottom of the page, the browser's default scroll behavior can conflict with the `BlockSelectionPlugin`. To mitigate this, you can add a non-selectable area to the right side of the editor:

If you encounter this issue, you might consider disabling it in this way.
```tsx
<div className="absolute right-0 top-0 h-full w-4 select-none" />
```

This helps prevent unexpected scrolling during selection operations.

## Plugins

Expand Down Expand Up @@ -266,6 +282,10 @@ A wrapper component that adds block selection functionality to its children.

## Hooks

### useBlockSelected

Returns true if context block is selected.

### useBlockSelectableState

<APIReturns>
Expand Down
28 changes: 25 additions & 3 deletions apps/www/content/docs/components/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,37 @@ Since Plate UI is not a component library, a changelog is maintained here.

Use the [CLI](https://platejs.org/docs/components/cli) to install the latest version of the components.

## October 2024 #15

### October 1 2024 #15.1
## October 2024 #15

### October 1 #15.1

- New `block-selection.tsx` component for visual selection feedback
- New `plate-element.tsx` component for rendering the plate element with `BlockSelection`
- Updated `paragraph-element.tsx` and all block elements to use `plate-element.tsx`
- `draggable.tsx`:
- Refactored to use new hooks: `useDraggableGutter` and `useDropLine`
- Removed `classNames` prop in favor of a single `className`
- Added `DraggableProvider` wrapper
- Introduced `Gutter` and `DropLine` as separate components
- `with-draggables.tsx`:
- Updated to use new className format for draggable props
- fix `mention-element`: prevent IME input interruption on MacOS

Use `--highlight` color for the following components:

- `comment-leaf.tsx`
- `highlight-leaf.tsx`

Use `--brand` color for the following components:

- `block-selection.tsx`
- `draggable.tsx`


## September 2024 #14

### September 29 2024 #14.3
### September 29 #14.3

- fix `heading-element`: if the heading is the first block, it should not have a top margin

Expand Down
80 changes: 48 additions & 32 deletions apps/www/content/docs/dnd.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ Enhances a component with draggable behavior.

## API Components

### DraggableProvider

A new component that provides context for managing draggable state.

<APIProps>
<APIItem name="children" type="React.ReactNode">
The child components to be wrapped with the draggable context.
</APIItem>
</APIProps>

### DndScroller

A wrapper component for the `Scroller` component that is conditionally rendered based on the dragging state.
Expand Down Expand Up @@ -290,10 +300,6 @@ A custom hook that enables dragging of a node from the editor using the `useDrag
A custom hook that provides the necessary properties and event handlers for making an element draggable.

<APIState>
<APIItem name="DropLine" type="'' | 'top' | 'bottom'">
The direction of the drop line, indicating the position where the node can
be dropped.
</APIItem>
<APIItem name="isDragging" type="boolean">
Indicates whether the node is currently being dragged.
</APIItem>
Expand All @@ -307,36 +313,11 @@ A custom hook that provides the necessary properties and event handlers for maki

<APIReturns>
<APIItem name="previewRef" type="React.RefObject">
A reference to the HTML `div` element that serves as the preview during
drag.
A reference to the HTML `div` element that serves as the preview during drag.
</APIItem>
<APIItem name="handleRef" type="ConnectDragSource">
A reference to the drag source connector provided by `react-dnd`.
</APIItem>
<APIItem name="droplineProps" type="object">
Additional props to be applied to the dropline element.
<APISubList>
<APISubListItem
parent="droplineProps"
name="contentEditable"
type="boolean"
>
Indicates whether the dropline element should be editable.
</APISubListItem>
</APISubList>
</APIItem>
<APIItem name="gutterLeftProps" type="object">
Additional props to be applied to the left gutter element.
<APISubList>
<APISubListItem
parent="gutterLeftProps"
name="contentEditable"
type="boolean"
>
Indicates whether the dropline element should be editable.
</APISubListItem>
</APISubList>
</APIItem>
</APIReturns>

### useDropBlock
Expand All @@ -347,7 +328,9 @@ A custom hook that enables dropping a block into the editor. It internally uses
<APIItem name="editor" type="PlateEditor">
The editor instance.
</APIItem>
<APIItem name="options" type="Omit<UseDropNodeOptions, 'accept'>"></APIItem>
<APIItem name="options" type="Omit<UseDropNodeOptions, 'accept'>">
Options for the drop behavior.
</APIItem>
</APIParameters>

### useDropNode
Expand Down Expand Up @@ -384,4 +367,37 @@ A custom hook that enables dropping a node on the editor. It uses the `useDrop`
</APISubList>

</APIItem>
</APIParameters>
</APIParameters>

### useDraggableGutter

Returns props for the draggable gutter.

<APIReturns>
<APIItem name="props" type="object">
Props to be spread on the gutter element.
</APIItem>
</APIReturns>

### useDropLine

Returns the current drop line state and props.

<APIReturns>
<APIItem name="dropLine" type="'top' | 'bottom' | null">
The current direction of the drop line.
</APIItem>
<APIItem name="props" type="object">
Props to be spread on the drop line element.
</APIItem>
</APIReturns>

### useDraggableStore

Draggable store.

<APIState>
<APIItem name="dropLine" type="'top' | 'bottom' | null">
The current direction of the drop line.
</APIItem>
</APIState>
Loading

0 comments on commit 2c179e8

Please sign in to comment.