-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
#7454: Down-casting whole table on headingRows attribute change #7572
Merged
Changes from 3 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
60e4ca7
Table headingRows attribute change triggers downcasting of the whole …
niegowski e749264
Added test for #7454 issue.
niegowski 66ebfe4
Fixed removing empty rows and columns when multiple rows and columns …
niegowski 2eb2a11
Removed redundant model.change block.
niegowski 17a7242
Added test for handling headingRows attribute in undo.
niegowski 70b5134
Added table post-fixer that marks table to refresh on headingRows att…
niegowski 13c9fd8
Update injectTableHeadingRowsRefreshPostFixer() documentation.
jodator 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 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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 |
---|---|---|
|
@@ -136,7 +136,7 @@ export default class TableUtils extends Plugin { | |
|
||
// Inserting rows inside heading section requires to update `headingRows` attribute as the heading section will grow. | ||
if ( headingRows > insertAt ) { | ||
writer.setAttribute( 'headingRows', headingRows + rowsToInsert, table ); | ||
updateNumericAttribute( 'headingRows', headingRows + rowsToInsert, table, writer, 0 ); | ||
} | ||
|
||
// Inserting at the end or at the beginning of a table doesn't require to calculate anything special. | ||
|
@@ -309,9 +309,8 @@ export default class TableUtils extends Plugin { | |
const rowsToRemove = options.rows || 1; | ||
const first = options.at; | ||
const last = first + rowsToRemove - 1; | ||
const batch = options.batch || 'default'; | ||
|
||
model.enqueueChange( batch, writer => { | ||
model.change( writer => { | ||
// Removing rows from the table require that most calculations to be done prior to changing table structure. | ||
// Preparations must be done in the same enqueueChange callback to use the current table structure. | ||
|
||
|
@@ -337,11 +336,15 @@ export default class TableUtils extends Plugin { | |
updateNumericAttribute( 'rowspan', rowspan, cell, writer ); | ||
} | ||
|
||
// 2d. Remove empty columns (without anchored cells) if there are any. | ||
removeEmptyColumns( table, this ); | ||
// 2d. Adjust heading rows if removed rows were in a heading section. | ||
updateHeadingRows( table, first, last, model ); | ||
|
||
// 2e. Adjust heading rows if removed rows were in a heading section. | ||
updateHeadingRows( table, first, last, model, batch ); | ||
// 2e. Remove empty columns (without anchored cells) if there are any. | ||
if ( !removeEmptyColumns( table, this ) ) { | ||
// If there wasn't any empty columns then we still need to check if this wasn't called | ||
// because of cleaning empty rows and we only removed one of them. | ||
removeEmptyRows( table, this ); | ||
} | ||
} ); | ||
} | ||
|
||
|
@@ -396,7 +399,11 @@ export default class TableUtils extends Plugin { | |
} | ||
|
||
// Remove empty rows that could appear after removing columns. | ||
removeEmptyRows( table, this, writer.batch ); | ||
if ( !removeEmptyRows( table, this ) ) { | ||
// If there wasn't any empty rows then we still need to check if this wasn't called | ||
// because of cleaning empty columns and we only removed one of them. | ||
removeEmptyColumns( table, this ); | ||
} | ||
} ); | ||
} | ||
|
||
|
@@ -776,13 +783,8 @@ function adjustHeadingColumns( table, removedColumnIndexes, writer ) { | |
} | ||
|
||
// Calculates a new heading rows value for removing rows from heading section. | ||
function updateHeadingRows( table, first, last, model, batch ) { | ||
// Must be done after the changes in table structure (removing rows). | ||
// Otherwise the downcast converter for headingRows attribute will fail. | ||
// See https://github.com/ckeditor/ckeditor5/issues/6391. | ||
// | ||
// Must be completely wrapped in enqueueChange to get the current table state (after applying other enqueued changes). | ||
model.enqueueChange( batch, writer => { | ||
function updateHeadingRows( table, first, last, model ) { | ||
model.change( writer => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that this is not needed any more - it is already called in a |
||
const headingRows = table.getAttribute( 'headingRows' ) || 0; | ||
|
||
if ( first < headingRows ) { | ||
|
This file contains 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
Oops, something went wrong.
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.
@scofalik & @Reinmar - I need to double check this as this changes how table is converted.
After numerous bugs with changing headings + removing rows we concluded that enough is enough. The table models sucks. Downcasting
headingRows
change requires moving<tr>
in the view from<thead>
to<tbody>
or vice-versa. It can fail in multiple scenarios (#6502, #6437, #6544, #6502, #6391, #6406 + #7454) and would potentially never be OK.Here (#7454) there was a problem with table layout post-fixer which was called after each
enqueueChange()
- and thus we hit limit of what API is allowing us to do. The solution is to wait with conversion and post-fixing after all table model changes are done and thattable
attribute converter would know that alsotable
children are changing.Possible considered solutions were:
table
to be re-rendered at once.We choose option
3
usingdiffer.refreshItem()
- which is called on everyheadingRows
attribute operation as It requires to also take external operations into account when dealing with this bug. Also the API for manyTableUtils
get simplified but not passing abatch
instance (:tada:).In other words the solution is to re-render whole table in the view if
headingRows
attribute was changed by any means.What is needed - some kind of 👍 / 👎 - it might impact collaboration features in some way. Do we need better API for requiring re-converting whole chunk of content?
ps.: @Reinmar this is also some sort of ADR proposal (without sections - but have intro, considered steps and proposed solution).
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.
Why this is done on
applyOperation
not in a post-fixer?As a rule of thumb, we try to avoid doing stuff on
applyOperation
- this is the last resort solution. Getting between multiple operations is a risky business. OTOH, I see that you only mark the table to refresh, so no changes in the model tree are done. Might be okay.The one difference that quickly comes to mind is that you might have an attribute operation for heading rows and then an operation that reverses it, so in the end, there's no change. Post-fixer would not refresh the table, AFAIU.
So, once again, if it can be done through post-fixer and there are no counterarguments, I think I'd go with post-fixer. But I don't see real big disadvantages in doing this with
applyOperation
either, other than we simply avoid it because it is a more vulnerable "place", if you know what I mean.