@@ -8,8 +8,13 @@ import { ImagePlaceholderPluginKey } from '@extensions/image/imageHelpers/imageP
88import { LinkedStylesPluginKey } from '@extensions/linked-styles/linked-styles.js' ;
99import { findParentNodeClosestToPos } from '@core/helpers/findParentNodeClosestToPos.js' ;
1010import { generateDocxRandomId } from '../../core/helpers/index.js' ;
11+ import { computePosition , autoUpdate , hide } from '@floating-ui/dom' ;
12+
13+ const SEPARATOR_CLASS = 'pagination-separator' ;
14+ const SEPARATOR_FLOATING_CLASS = 'pagination-separator-floating' ;
1115
1216const isDebugging = false ;
17+ const cleanupFunctions = new Set ( ) ;
1318
1419export const Pagination = Extension . create ( {
1520 name : 'pagination' ,
@@ -42,6 +47,7 @@ export const Pagination = Extension.create({
4247 */
4348 addPmPlugins ( ) {
4449 const editor = this . editor ;
50+
4551 let isUpdating = false ;
4652
4753 // Used to prevent unnecessary transactions
@@ -129,7 +135,7 @@ export const Pagination = Extension.create({
129135 let previousDecorations = DecorationSet . empty ;
130136
131137 return {
132- update : ( view ) => {
138+ update : ( view , prevState ) => {
133139 if ( ! shouldUpdate || isUpdating ) return ;
134140
135141 isUpdating = true ;
@@ -141,6 +147,7 @@ export const Pagination = Extension.create({
141147 */
142148 if ( isDebugging ) console . debug ( '--- Calling performUpdate ---' )
143149 performUpdate ( editor , view , previousDecorations ) ;
150+
144151 isUpdating = false ;
145152 shouldUpdate = false ;
146153 } ,
@@ -155,6 +162,10 @@ export const Pagination = Extension.create({
155162
156163 return [ paginationPlugin ] ;
157164 } ,
165+
166+ onDestroy ( ) {
167+ cleanupFloatingSeparators ( ) ;
168+ } ,
158169} ) ;
159170
160171/**
@@ -218,8 +229,9 @@ const getHeaderFooterId = (currentPageNumber, sectionType, editor, node = null)
218229 * @returns {void }
219230 */
220231const performUpdate = ( editor , view , previousDecorations ) => {
221- const sectionData = editor . storage . pagination . sectionData ;
232+ const sectionData = editor . storage . pagination . sectionData ;
222233 const newDecorations = calculatePageBreaks ( view , editor , sectionData ) ;
234+ const editorElement = editor . options . element ;
223235
224236 // Skip updating if decorations haven't changed
225237 if ( ! previousDecorations . eq ( newDecorations ) ) {
@@ -229,7 +241,18 @@ const performUpdate = (editor, view, previousDecorations) => {
229241 ) ;
230242
231243 view . dispatch ( updateTransaction ) ;
232- }
244+
245+ requestAnimationFrame ( ( ) => {
246+ requestAnimationFrame ( ( ) => {
247+ cleanupFloatingSeparators ( ) ;
248+ const separators = [ ...editorElement . querySelectorAll ( `.${ SEPARATOR_CLASS } --table` ) ] ;
249+ separators . forEach ( ( separator ) => {
250+ const { cleanup } = createFloatingSeparator ( separator ) ;
251+ cleanupFunctions . add ( cleanup ) ;
252+ } ) ;
253+ } ) ;
254+ } ) ;
255+ } ;
233256
234257 // Emit that pagination has been updated
235258 editor . emit ( 'paginationUpdate' ) ;
@@ -370,8 +393,11 @@ function generateInternalPageBreaks(doc, view, editor, sectionData) {
370393
371394 if ( isHardBreakNode || shouldAddPageBreak ) {
372395 const $currentPos = view . state . doc . resolve ( currentPos ) ;
396+ const table = findParentNodeClosestToPos ( $currentPos , ( node ) => node . type . name === 'table' ) ;
373397 const tableRow = findParentNodeClosestToPos ( $currentPos , ( node ) => node . type . name === 'tableRow' ) ;
374398
399+ let isInTable = ( table || tableRow ) ? true : false ;
400+
375401 if ( tableRow ) {
376402 // If the node is in a table cell, then split the entire row.
377403 currentNode = tableRow . node ;
@@ -408,7 +434,7 @@ function generateInternalPageBreaks(doc, view, editor, sectionData) {
408434 const pageSpacer = Decoration . widget ( breakPos , spacingNode , { key : 'stable-key' } ) ;
409435 decorations . push ( pageSpacer ) ;
410436
411- const pageBreak = createPageBreak ( { editor, header, footer } ) ;
437+ const pageBreak = createPageBreak ( { editor, header, footer, isInTable } ) ;
412438 decorations . push ( Decoration . widget ( breakPos , pageBreak , { key : 'stable-key' } ) ) ;
413439
414440 // Check if we have a hard page break node
@@ -637,7 +663,7 @@ const onHeaderFooterDblClick = (editor, currentFocusedSectionEditor) => {
637663 * @param {HTMLElement } param0.footer The footer element
638664 * @returns {HTMLElement } The page break element
639665 */
640- function createPageBreak ( { editor, header, footer, footerBottom = null , isFirstHeader, isLastFooter } ) {
666+ function createPageBreak ( { editor, header, footer, footerBottom = null , isFirstHeader, isLastFooter, isInTable = false } ) {
641667 const { pageSize, pageMargins } = editor . converter . pageStyles ;
642668
643669 let sectionHeight = 0 ;
@@ -661,9 +687,11 @@ function createPageBreak({ editor, header, footer, footerBottom = null, isFirstH
661687 const separatorHeight = 20 ;
662688 sectionHeight += separatorHeight ;
663689 const separator = document . createElement ( 'div' ) ;
664- separator . className = 'pagination-separator' ;
690+ separator . classList . add ( SEPARATOR_CLASS ) ;
691+ if ( isInTable ) {
692+ separator . classList . add ( `${ SEPARATOR_CLASS } --table` ) ;
693+ }
665694 if ( isDebugging ) separator . style . backgroundColor = 'green' ;
666-
667695 innerDiv . appendChild ( separator ) ;
668696 }
669697
@@ -731,3 +759,70 @@ const onImageLoad = (editor) => {
731759 editor . view . dispatch ( newTr ) ;
732760 } ) ;
733761} ;
762+
763+ function createFloatingSeparator ( separator ) {
764+ const floatingSeparator = document . createElement ( 'div' ) ;
765+ floatingSeparator . classList . add ( SEPARATOR_FLOATING_CLASS ) ;
766+ floatingSeparator . dataset . floatingSeparator = '' ;
767+
768+ document . body . append ( floatingSeparator ) ;
769+
770+ const updatePosition = ( ) => {
771+ computePosition ( separator , floatingSeparator , {
772+ strategy : 'fixed' ,
773+ placement : 'top-start' ,
774+ middleware : [
775+ hide ( {
776+ padding : {
777+ top : 21 ,
778+ bottom : 21 ,
779+ } ,
780+ } ) ,
781+ {
782+ name : 'copy' ,
783+ fn : ( { elements } ) => {
784+ const rect = elements . reference . getBoundingClientRect ( ) ;
785+ return {
786+ x : rect . left ,
787+ y : rect . top ,
788+ data : {
789+ width : rect . width ,
790+ height : rect . height ,
791+ } ,
792+ } ;
793+ } ,
794+ } ,
795+ ] ,
796+ } ) . then ( ( { x, y, middlewareData } ) => {
797+ Object . assign ( floatingSeparator . style , {
798+ top : `${ y } px` ,
799+ left : `${ x } px` ,
800+ width : `${ middlewareData . copy . width } px` ,
801+ height : `${ middlewareData . copy . height } px` ,
802+ visibility : middlewareData . hide ?. referenceHidden ? 'hidden' : 'visible' ,
803+ } ) ;
804+ } ) ;
805+ } ;
806+
807+ const cleanup = autoUpdate (
808+ separator ,
809+ floatingSeparator ,
810+ updatePosition ,
811+ // { animationFrame: true },
812+ ) ;
813+
814+ const extendedCleanup = ( ) => {
815+ floatingSeparator ?. remove ( ) ;
816+ cleanup ( ) ;
817+ } ;
818+
819+ return {
820+ cleanup : extendedCleanup ,
821+ updatePosition,
822+ } ;
823+ }
824+
825+ function cleanupFloatingSeparators ( ) {
826+ cleanupFunctions . forEach ( ( cleanup ) => cleanup ( ) ) ;
827+ cleanupFunctions . clear ( ) ;
828+ }
0 commit comments