Skip to content

[lexical-table] Feature: Improve logic for pasting table into table#7408

Merged
etrepum merged 3 commits intomainfrom
takuyakanbr/table-pasting
Apr 1, 2025
Merged

[lexical-table] Feature: Improve logic for pasting table into table#7408
etrepum merged 3 commits intomainfrom
takuyakanbr/table-pasting

Conversation

@takuyakanbr
Copy link
Copy Markdown
Contributor

@takuyakanbr takuyakanbr commented Mar 30, 2025

Description

Improve how we handle pasting table into table:

  • Properly account for merged cells in both tables. Presence of merged cells currently causes very buggy behavior when pasting (including the possibility of creating nested tables).
  • If the current selection is a range selection, allow the table being modified to grow if necessary to contain the table being pasted. This is in line with GDocs behavior.
  • If the current selection is a table selection, constrain the modifications to be within the selection boundary. This is in line with GDocs behavior.

Closes #7262
Closes #7263

Test plan

Added E2E tests for table pasting: npm run test-e2e-chromium Tables.spec

Before

table-pasting__before.mov

After

table-pasting__after.mov

Additional notes

The new logic for pasting tables can be summarized as:

  1. Unmerge all cells in affected area of the table being modified.
  2. Expand the table being modified so that it can contain the table being pasted (if range selection).
  3. Merge cells in the table being modified to match the table being pasted.
  4. Set content in all affected cells to match the table being pasted.

To support the new logic, I've added a few additional table utils (in lexical-table package) that works on TableCellNode(s)—insert row, insert column, merge cells, unmerge cell. The logic for merging cells is taken from the TableActionMenuPlugin in lexical-playground package.

Some potential followups:

  • I can't seem to be able to trigger pasting using cmd+V when there is a table selection (on Chrome). You can still use the browser context menu, but this should probably be fixed.
  • Unlike GDocs, I'm not currently making multiple copies of the table being pasted when the selection boundary is larger than the size of the table being pasted. If there is interest in this, it could be a potential follow-up item.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
lexical ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 1, 2025 8:50pm
lexical-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 1, 2025 8:50pm

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Mar 30, 2025
* taking into account any spans. If successful, returns the
* inserted table row node.
*/
export function $insertTableRowAtNode__EXPERIMENTAL(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The "experimental" tag for insert row/column follows the existing functions that they are being extracted from. Open qns: Those function were added 2 years ago, so I'm wondering if we could promote them to non-experimental and get rid of the legacy versions.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend exporting them with the non-experimental names and maintaining the experimental names for some period of time with exports with @deprecated annotations

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case for new functions I don't think they should have these experimental names, if a function is experimental it would be better to just document it appropriately (e.g. with @internal if we don't want it to show up in docs). I think this naming convention was a failed experiment and not a convention worth following for new code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've renamed the new functions. For the existing "experimental" insert row/column functions, there are legacy exported functions using the same name (without the __EXPERIMENTAL tag) but with different APIs 😅

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hah! well I guess we need to come up with new names then, would be hard to do a proper deprecation if the call signature changes. Doesn't have to be in the scope of this PR

@takuyakanbr takuyakanbr changed the title [WIP] [lexical-table] Feature: Improve logic for pasting table into table [lexical-table] Feature: Improve logic for pasting table into table Apr 1, 2025
@takuyakanbr takuyakanbr marked this pull request as ready for review April 1, 2025 08:18
@etrepum etrepum added the extended-tests Run extended e2e tests on a PR label Apr 1, 2025
@etrepum
Copy link
Copy Markdown
Collaborator

etrepum commented Apr 1, 2025

While you're in this part of the code there's another issue with one of these table functions that is probably a simple fix: #7140

@etrepum
Copy link
Copy Markdown
Collaborator

etrepum commented Apr 1, 2025

Regarding the paste with cmd+V I think the problem is that the browser needs to have an active selection in order to handle paste. I think the real solution to this problem will be to create a selection in the browser that is promptly removed or otherwise specifically ignored by all of the code that tries to reconcile a change in the browser's selection to a new lexical RangeSelection (e.g. which happens while starting every update unless certain conditions are met, plus the selectionchange listener). This is probably also relevant to NodeSelection or any other custom selection. What may be needed is another hack like BlockCursorElement but for holding the browser's selection while a non-RangeSelection is in use.

Copy link
Copy Markdown
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do a careful audit of the code or tests but I did some manual testing and it seems to work much better now. Will approve once the functions are renamed without the EXPERIMENTAL postfix.

@etrepum etrepum added this pull request to the merge queue Apr 1, 2025
Merged via the queue into main with commit aa06166 Apr 1, 2025
45 checks passed
@takuyakanbr takuyakanbr deleted the takuyakanbr/table-pasting branch April 2, 2025 06:15
@etrepum etrepum mentioned this pull request Apr 7, 2025
GermanJablo added a commit to payloadcms/payload that referenced this pull request Sep 1, 2025
GermanJablo added a commit to payloadcms/payload that referenced this pull request Sep 3, 2025
Fixes #13386

Below I write a clarification to copy and paste into the release note,
based on our latest upgrade of Lexical [in
v3.29.0](https://github.com/payloadcms/payload/releases/tag/v3.29.0).

## Important
This release upgrades the lexical dependency from 0.28.0 to 0.34.0.

If you installed lexical manually, update it to 0.34.0. Installing
lexical manually is not recommended, as it may break between updates,
and our re-exported versions should be used. See the [yellow banner
box](https://payloadcms.com/docs/rich-text/custom-features) for details.

If you still encounter richtext-lexical errors, do the following, in
this order:

- Delete node_modules
- Delete your lockfile (e.g. pnpm-lock.json)
- Reinstall your dependencies (e.g. pnpm install)

### Lexical Breaking Changes

The following Lexical releases describe breaking changes. We recommend
reading them if you're using Lexical APIs directly
(`@payloadcms/richtext-lexical/lexical/*`).

- [v.0.33.0](https://github.com/facebook/lexical/releases/tag/v0.33.0)
- [v.0.30.0](https://github.com/facebook/lexical/releases/tag/v0.30.0)
- [v.0.29.0](https://github.com/facebook/lexical/releases/tag/v0.29.0)

___

TODO:
- [x] facebook/lexical#7719
- [x] facebook/lexical#7362
- [x] facebook/lexical#7707
- [x] facebook/lexical#7388
- [x] facebook/lexical#7357
- [x] facebook/lexical#7352
- [x] facebook/lexical#7472
- [x] facebook/lexical#7556
- [x] facebook/lexical#7417
- [x] facebook/lexical#1036
- [x] facebook/lexical#7509
- [x] facebook/lexical#7693
- [x] facebook/lexical#7408
- [x] facebook/lexical#7450
- [x] facebook/lexical#7415
- [x] facebook/lexical#7368
- [x] facebook/lexical#7372
- [x] facebook/lexical#7572
- [x] facebook/lexical#7558
- [x] facebook/lexical#7613
- [x] facebook/lexical#7405
- [x] facebook/lexical#7420
- [x] facebook/lexical#7662

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211202581885926
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

3 participants