-
Notifications
You must be signed in to change notification settings - Fork 3
feat: rfc for supporting tables in excalidraw #1
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
Open
ad1992
wants to merge
16
commits into
main
Choose a base branch
from
aakansha/tables
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+612
−1
Open
Changes from 6 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
bdeb4b3
feat: rfc for supporting tables in excalidraw
ad1992 cac1ad4
add missing details
ad1992 5c2ba88
update rfcs with more details
ad1992 fd5bd54
remove tableId as its not needed rn
ad1992 423a7eb
Merge remote-tracking branch 'origin/main' into aakansha/tables
ad1992 9210ee5
add virtual cell elements alternative approach
ad1992 55182b4
tweak RFC
ad1992 cccd166
add alternate approach to ease reordering of rows/columns
ad1992 3ba9c82
add order ad timestamp to resolve conflicts
ad1992 5b4f16e
add lastUpdated
ad1992 38deb78
rename order to index
ad1992 a5cf5be
remove trailing quotes
ad1992 8b02bee
update the rfc with the latest structure and updates
ad1992 a864537
uplift width and height to respective column and row attributes
ad1992 256f318
use for each row/column and cell and combine with top level versionN…
ad1992 31d7c7e
fix typos
ad1992 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,238 @@ | ||
| - Start Date: 2024-07-13 | ||
| - Referenced Issues: https://github.com/excalidraw/excalidraw/issues/4847 | ||
| - Implementation PR: (leave this empty) | ||
|
|
||
| # Summary | ||
|
|
||
| Once this feature is implemented the users will be able to use tables in excalidraw. | ||
|
|
||
| # Motivation | ||
|
|
||
| Tables are one of the most heavily requested features in Excalidraw for past few years. This will also be a very useful feature as well hence we should consider pushing it out soon. | ||
|
|
||
| # Detailed Design | ||
|
|
||
| Tables are nothing but a collection of Text Containers next to each other in form of a grid with `x` rows and `y` columns. | ||
|
|
||
| Lets see how the data structure of table element look like. | ||
|
|
||
| A `table` element will have `rows` and `columns` defining the grid. The `table` will also have `x` , `y` coordinates for positioning on canvas and also the `width` and `height` for dimensions. | ||
|
|
||
| The `table` element consists of multiple cells. Each cell will have an `id`, `x` and `y` coordinates and dimensions as well since each cell's `width/height` can be varied and some text. Each cell is a text container (rectangle with bound text). | ||
|
|
||
| Below is the sample JSON representation of a `table` element | ||
|
|
||
| ```js | ||
| { | ||
| "id": "table-id", | ||
| "type": "table", | ||
| "x": 100, | ||
| "y": 100, | ||
| "title": "Table Title", | ||
| "rows": 2, | ||
| "columns": 2, | ||
| "cells": [ | ||
| { | ||
| "id": "cell-1", | ||
| "row": 0, | ||
| "column": 0, | ||
| "x": 100, | ||
| "y": 100, | ||
| "width": 100, | ||
| "height": 50, | ||
| "boundElements": [{id: "text-1", type: "text"}] | ||
| }, | ||
| { | ||
| "id": "cell-2", | ||
| "x": 200, | ||
| "y": 100, | ||
| "row": 0, | ||
| "column": 1, | ||
| "width": 150, | ||
| "height": 50, | ||
| "boundElements": [{id: "text-2", type: "text"}] | ||
| }, | ||
| { | ||
| "id": "cell-3", | ||
| "x": 100, | ||
| "y": 150, | ||
| "row": 1, | ||
| "column": 0, | ||
| "width": 120, | ||
| "height": 60, | ||
| "boundElements": [{id: "text3", type: "text"}] | ||
| }, | ||
| { | ||
| "id": "cell-4", | ||
| "x": 220, | ||
| "y": 150, | ||
| "row": 1, | ||
| "column": 1, | ||
| "width": 130, | ||
| "height": 60, | ||
| "boundElements": [{id: "text-4", type: "text"}] | ||
| } | ||
| ], | ||
| "angle": 0, | ||
| "strokeColor": "#000000", | ||
| "backgroundColor": "transparent", | ||
| "fillStyle": "hachure", | ||
| "strokeWidth": 1, | ||
| "strokeStyle": "solid", | ||
| "roughness": 0, | ||
| "opacity": 100, | ||
| "groupIds": [], | ||
| "seed": 1, | ||
| "version": 1, | ||
| "versionNonce": 1, | ||
| "isDeleted": false, | ||
| "boundElements": null, | ||
| "link": null, | ||
| "locked": false | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| A table will have a `title` as well and the count of `rows` and `columns` as well. | ||
|
|
||
| The table consists of `cells` defining the content of each cell. | ||
| Each cell has an `id`, `x`, `y` and dimensions. Since when resizing the width and height of cells can be altered hence recording the dimensions of each cell. | ||
|
|
||
| The `row` and `column` helps in identifying the row and column indexes of the cell. | ||
|
|
||
| Each cell has a `boundElements` to keep it in sync with text containers / labeled arrows, however we can surely rename it to `textId` to keep it simple. | ||
|
|
||
| ## Adding and Deleting rows / columns | ||
|
|
||
| To start with we can keep it simple and just allow to add | ||
| 3 x 3 grid table. Once table is added we can have a add row and add column button to increase the rows and columns similar to how **Miro** has. | ||
|
|
||
|  | ||
|
|
||
| Similarly and entire `row` and `column` can be removed by right clicking a cell. We just set the attribute `isDeleted` to true for all cells in that row / column and since we are storing the `row` and `column` index in each cell, it will be easy to identify which cells to remove. | ||
|
|
||
| ## Interacting with tables | ||
|
|
||
| Similar to other shape tool, the table tool will be resizable, movable etc. | ||
|
|
||
| Whenever user clicks on the cell - show a text box to updated or add the text. The text will start wrapping as per the cell dimensions similar to how it works for text container. | ||
|
|
||
| Additionally if a user clicks on the cell stroke (width / height) should be adjustable. So we need to do a hit test whether the cell stroke is being hit and allow the user to adjust dimensions. | ||
| But since this would be internally using rectangles so this should be possible without custom code. | ||
| What we need to support is allow the width / height of all cells in that row /column when any one of them is moved. | ||
|
|
||
| Since the table is not just a single element but a collection of different elements, whenever there is an operation eg moving , resizing, we need to update all the children of the table (basically every cell). | ||
|
|
||
| ## Supporting row and column headers | ||
|
|
||
| Ideally the first row and column should be preserved for the row and column header. But do we need a separate distinction for headers ? (Eg showing header in diff background color or some highlighter how miro does it). | ||
|
|
||
| I think the users will be able to style the headers differently once we have support for wyswyg editor, so till then lets keep it simple. | ||
|
|
||
| ## Alternatives | ||
|
|
||
| In the above approach the text elements with `containerId` as `cell id` will exists in the `json`. This means there will be 4 separate `text` elements in the `json`. | ||
|
|
||
| Here is an alternative version - Having `cells` as virtual text elements instead of actual text elements. | ||
|
|
||
| ```js | ||
| { | ||
| "id": "table-id", | ||
| "type": "table", | ||
| "x": 100, | ||
| "y": 100, | ||
| "title": "Table Title", | ||
| "rows": 2, | ||
| "columns": 2, | ||
| "cells": [ | ||
| { | ||
| "id": "cell-1", | ||
| "row": 0, | ||
| "column": 0, | ||
| "x": 100, | ||
| "y": 100, | ||
| "width": 100, | ||
| "height": 50, | ||
| "text":"Cell 1", | ||
| "fontSize": 16; | ||
| "fontFamily": 1; | ||
|
|
||
| }, | ||
| { | ||
| "id": "cell-2", | ||
| "x": 200, | ||
| "y": 100, | ||
| "row": 0, | ||
| "column": 1, | ||
| "width": 150, | ||
| "height": 50, | ||
| "text":"Cell 2", | ||
| "fontSize": 16; | ||
| "fontFamily": 1; | ||
| }, | ||
| { | ||
| "id": "cell-3", | ||
| "x": 100, | ||
| "y": 150, | ||
| "row": 1, | ||
| "column": 0, | ||
| "width": 120, | ||
| "height": 60, | ||
| "text":"Cell 3", | ||
| "fontSize": 16; | ||
| "fontFamily": 1; | ||
| }, | ||
| { | ||
| "id": "cell-4", | ||
| "x": 220, | ||
| "y": 150, | ||
| "row": 1, | ||
| "column": 1, | ||
| "width": 130, | ||
| "height": 60, | ||
| "text":"Cell 4", | ||
| "fontSize": 16; | ||
| "fontFamily": 1; | ||
| } | ||
| ], | ||
| "angle": 0, | ||
| "strokeColor": "#000000", | ||
| "backgroundColor": "transparent", | ||
| "fillStyle": "hachure", | ||
| "strokeWidth": 1, | ||
| "strokeStyle": "solid", | ||
| "roughness": 0, | ||
| "opacity": 100, | ||
| "groupIds": [], | ||
| "seed": 1, | ||
| "version": 1, | ||
| "versionNonce": 1, | ||
| "isDeleted": false, | ||
| "boundElements": null, | ||
| "link": null, | ||
| "locked": false | ||
| } | ||
| ``` | ||
| As you can see above the `cells` will contain all the attributes (only some of text attributes are shown above) and at the time of rendering these text elements will be drawn, however we won't be storing the text elements as separate elements. | ||
| This would simplify the data structure and reduce the hierarchy, thus helping in better maintainance | ||
|
|
||
| ## Questions | ||
|
|
||
| ## For Virtual Cell Elements | ||
|
|
||
| When `cells` are virtual elements which are just rendered on canvas but physically they don't exist in json? This will definately simplify the data structure, however there are some unknowns listed below. | ||
|
|
||
| - With above approach of virtual elements, how will impact the overall performance of interacting with tables since we heavily rely on actual elements ? | ||
| - Need to verify if this approach is feasable with current state of TextWYSIWYG | ||
| - How will this impact collaboration ? We may need a custom logic for tables? | ||
|
|
||
| # Adoption strategy | ||
|
|
||
| - If we implement this proposal, how will existing Excalidraw users adopt it? | ||
|
|
||
| This is a new feature so adoption should be straight forward and they will be using it as needed. | ||
|
|
||
| - Is this a breaking change? If yes how are migrating the existing Excalidraw users ? | ||
|
|
||
| No this is not a breaking change (unless we change some existing implementation) so hopefully no migration will be needed. | ||
| ``` | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dwelle, as per my understanding, can you confirm if this is what you meant by
virtualelements? These elements would be drawn at the time of rendering so there will be no existence of actual separate text elements