@@ -263,6 +263,120 @@ export const movePatternBetweenPages = (
263263 } ;
264264} ;
265265
266+ /**
267+ * Copies a page from a blueprint by creating a duplicate with a new ID.
268+ * This also copies all patterns contained within the page and updates their references.
269+ *
270+ */
271+ export const copyPage = (
272+ bp : Blueprint ,
273+ pageId : PatternId
274+ ) : { bp : Blueprint ; pattern : PagePattern } => {
275+ const pagePattern = bp . patterns [ pageId ] as PagePattern ;
276+ if ( ! pagePattern || pagePattern . type !== 'page' ) {
277+ throw new Error ( `Pattern with id ${ pageId } is not a page.` ) ;
278+ }
279+
280+ const newPageId = generatePatternId ( ) ;
281+ const timestamp = new Date ( ) . toLocaleString ( ) ;
282+
283+ const newPage : PagePattern = {
284+ ...pagePattern ,
285+ id : newPageId ,
286+ data : {
287+ ...pagePattern . data ,
288+ title : `${ pagePattern . data . title } Copy - ${ timestamp } ` ,
289+ patterns : [ ] ,
290+ } ,
291+ } ;
292+
293+ let updatedBp : Blueprint = {
294+ ...bp ,
295+ patterns : {
296+ ...bp . patterns ,
297+ [ newPageId ] : newPage ,
298+ } ,
299+ } ;
300+
301+ const idMap = new Map < PatternId , PatternId > ( ) ;
302+
303+ const copyPatternAndChildren = (
304+ currentBp : Blueprint ,
305+ patternId : PatternId
306+ ) : { bp : Blueprint ; newId : PatternId } => {
307+ const originalPattern = currentBp . patterns [ patternId ] ;
308+ if ( ! originalPattern ) {
309+ throw new Error ( `Pattern with id ${ patternId } not found` ) ;
310+ }
311+
312+ if ( idMap . has ( patternId ) ) {
313+ return { bp : currentBp , newId : idMap . get ( patternId ) as PatternId } ;
314+ }
315+
316+ const newId = generatePatternId ( ) ;
317+ idMap . set ( patternId , newId ) ;
318+
319+ const newPattern : Pattern = {
320+ ...originalPattern ,
321+ id : newId ,
322+ data : { ...originalPattern . data } ,
323+ } ;
324+
325+ let resultBp = {
326+ ...currentBp ,
327+ patterns : {
328+ ...currentBp . patterns ,
329+ [ newId ] : newPattern ,
330+ } ,
331+ } ;
332+
333+ if (
334+ ( newPattern . type === 'fieldset' || newPattern . type === 'repeater' ) &&
335+ Array . isArray ( originalPattern . data . patterns )
336+ ) {
337+ const newChildren : PatternId [ ] = [ ] ;
338+
339+ for ( const childId of originalPattern . data . patterns ) {
340+ const { bp : updatedBp , newId : newChildId } = copyPatternAndChildren (
341+ resultBp ,
342+ childId
343+ ) ;
344+ resultBp = updatedBp ;
345+ newChildren . push ( newChildId ) ;
346+ }
347+ resultBp . patterns [ newId ] . data . patterns = newChildren ;
348+ }
349+ return { bp : resultBp , newId } ;
350+ } ;
351+
352+ for ( const patternId of pagePattern . data . patterns ) {
353+ if ( bp . patterns [ patternId ] ) {
354+ const { bp : newState } = copyPatternAndChildren ( updatedBp , patternId ) ;
355+ updatedBp = newState ;
356+ }
357+ }
358+
359+ newPage . data . patterns = pagePattern . data . patterns . map (
360+ id => idMap . get ( id ) || id
361+ ) ;
362+ updatedBp . patterns [ newPageId ] = newPage ;
363+
364+ const pageSet = updatedBp . patterns [ updatedBp . root ] as PageSetPattern ;
365+ if ( pageSet . type === 'page-set' ) {
366+ updatedBp . patterns [ pageSet . id ] = {
367+ ...pageSet ,
368+ data : {
369+ pages : [ ...pageSet . data . pages , newPageId ] ,
370+ } ,
371+ } as PageSetPattern ;
372+ }
373+
374+ return {
375+ bp : updatedBp ,
376+ pattern : updatedBp . patterns [ newPageId ] as PagePattern ,
377+ } ;
378+ } ;
379+
266380/**
267381 * Copies a pattern from a blueprint by creating a duplicate with a new ID.
268382 *
0 commit comments