@@ -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 , editor ) ;
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,75 @@ const onImageLoad = (editor) => {
731759 editor . view . dispatch ( newTr ) ;
732760 } ) ;
733761} ;
762+
763+ function createFloatingSeparator ( separator , editor ) {
764+ const floatingSeparator = document . createElement ( 'div' ) ;
765+ floatingSeparator . classList . add ( SEPARATOR_FLOATING_CLASS ) ;
766+ floatingSeparator . dataset . floatingSeparator = '' ;
767+
768+ const { paginationFloatingClass } = editor . options ;
769+ if ( paginationFloatingClass ) {
770+ floatingSeparator . classList . add ( paginationFloatingClass ) ;
771+ }
772+
773+ document . body . append ( floatingSeparator ) ;
774+
775+ const updatePosition = ( ) => {
776+ computePosition ( separator , floatingSeparator , {
777+ strategy : 'fixed' ,
778+ placement : 'top-start' ,
779+ middleware : [
780+ hide ( {
781+ padding : {
782+ top : 21 ,
783+ bottom : 21 ,
784+ } ,
785+ } ) ,
786+ {
787+ name : 'copy' ,
788+ fn : ( { elements } ) => {
789+ const rect = elements . reference . getBoundingClientRect ( ) ;
790+ return {
791+ x : rect . left ,
792+ y : rect . top ,
793+ data : {
794+ width : rect . width ,
795+ height : rect . height ,
796+ } ,
797+ } ;
798+ } ,
799+ } ,
800+ ] ,
801+ } ) . then ( ( { x, y, middlewareData } ) => {
802+ Object . assign ( floatingSeparator . style , {
803+ top : `${ y } px` ,
804+ left : `${ x } px` ,
805+ width : `${ middlewareData . copy . width } px` ,
806+ height : `${ middlewareData . copy . height } px` ,
807+ visibility : middlewareData . hide ?. referenceHidden ? 'hidden' : 'visible' ,
808+ } ) ;
809+ } ) ;
810+ } ;
811+
812+ const cleanup = autoUpdate (
813+ separator ,
814+ floatingSeparator ,
815+ updatePosition ,
816+ // { animationFrame: true },
817+ ) ;
818+
819+ const extendedCleanup = ( ) => {
820+ floatingSeparator ?. remove ( ) ;
821+ cleanup ( ) ;
822+ } ;
823+
824+ return {
825+ cleanup : extendedCleanup ,
826+ updatePosition,
827+ } ;
828+ }
829+
830+ function cleanupFloatingSeparators ( ) {
831+ cleanupFunctions . forEach ( ( cleanup ) => cleanup ( ) ) ;
832+ cleanupFunctions . clear ( ) ;
833+ }
0 commit comments