99import { BlockCopyData , BlockPaster } from './clipboard/block_paster.js' ;
1010import * as registry from './clipboard/registry.js' ;
1111import type { ICopyData , ICopyable } from './interfaces/i_copyable.js' ;
12+ import { isSelectable } from './interfaces/i_selectable.js' ;
1213import * as globalRegistry from './registry.js' ;
1314import { Coordinate } from './utils/coordinate.js' ;
1415import { WorkspaceSvg } from './workspace_svg.js' ;
@@ -18,18 +19,119 @@ let stashedCopyData: ICopyData | null = null;
1819
1920let stashedWorkspace : WorkspaceSvg | null = null ;
2021
22+ let stashedCoordinates : Coordinate | undefined = undefined ;
23+
2124/**
22- * Private version of copy for stubbing in tests.
25+ * Copy a copyable item, and record its data and the workspace it was
26+ * copied from.
27+ *
28+ * This function does not perform any checks to ensure the copy
29+ * should be allowed, e.g. to ensure the block is deletable. Such
30+ * checks should be done before calling this function.
31+ *
32+ * Note that if the copyable item is not an `ISelectable` or its
33+ * `workspace` property is not a `WorkspaceSvg`, the copy will be
34+ * successful, but there will be no saved workspace data. This will
35+ * impact the ability to paste the data unless you explictily pass
36+ * a workspace into the paste method.
37+ *
38+ * @param toCopy item to copy.
39+ * @param location location to save as a potential paste location.
40+ * @returns the copied data if copy was successful, otherwise null.
2341 */
24- function copyInternal < T extends ICopyData > ( toCopy : ICopyable < T > ) : T | null {
42+ export function copy < T extends ICopyData > (
43+ toCopy : ICopyable < T > ,
44+ location ?: Coordinate ,
45+ ) : T | null {
2546 const data = toCopy . toCopyData ( ) ;
2647 stashedCopyData = data ;
27- stashedWorkspace = ( toCopy as any ) . workspace ?? null ;
48+ if ( isSelectable ( toCopy ) && toCopy . workspace instanceof WorkspaceSvg ) {
49+ stashedWorkspace = toCopy . workspace ;
50+ } else {
51+ stashedWorkspace = null ;
52+ }
53+
54+ stashedCoordinates = location ;
2855 return data ;
2956}
3057
3158/**
32- * Paste a pasteable element into the workspace.
59+ * Gets the copy data for the last item copied. This is useful if you
60+ * are implementing custom copy/paste behavior. If you want the default
61+ * behavior, just use the copy and paste methods directly.
62+ *
63+ * @returns copy data for the last item copied, or null if none set.
64+ */
65+ export function getLastCopiedData ( ) {
66+ return stashedCopyData ;
67+ }
68+
69+ /**
70+ * Sets the last copied item. You should call this method if you implement
71+ * custom copy behavior, so that other callers are working with the correct
72+ * data. This method is called automatically if you use the built-in copy
73+ * method.
74+ *
75+ * @param copyData copy data for the last item copied.
76+ */
77+ export function setLastCopiedData ( copyData : ICopyData ) {
78+ stashedCopyData = copyData ;
79+ }
80+
81+ /**
82+ * Gets the workspace that was last copied from. This is useful if you
83+ * are implementing custom copy/paste behavior and want to paste on the
84+ * same workspace that was copied from. If you want the default behavior,
85+ * just use the copy and paste methods directly.
86+ *
87+ * @returns workspace that was last copied from, or null if none set.
88+ */
89+ export function getLastCopiedWorkspace ( ) {
90+ return stashedWorkspace ;
91+ }
92+
93+ /**
94+ * Sets the workspace that was last copied from. You should call this method
95+ * if you implement custom copy behavior, so that other callers are working
96+ * with the correct data. This method is called automatically if you use the
97+ * built-in copy method.
98+ *
99+ * @param workspace workspace that was last copied from.
100+ */
101+ export function setLastCopiedWorkspace ( workspace : WorkspaceSvg ) {
102+ stashedWorkspace = workspace ;
103+ }
104+
105+ /**
106+ * Gets the location that was last copied from. This is useful if you
107+ * are implementing custom copy/paste behavior. If you want the
108+ * default behavior, just use the copy and paste methods directly.
109+ *
110+ * @returns last saved location, or null if none set.
111+ */
112+ export function getLastCopiedLocation ( ) {
113+ return stashedCoordinates ;
114+ }
115+
116+ /**
117+ * Sets the location that was last copied from. You should call this method
118+ * if you implement custom copy behavior, so that other callers are working
119+ * with the correct data. This method is called automatically if you use the
120+ * built-in copy method.
121+ *
122+ * @param location last saved location, which can be used to paste at.
123+ */
124+ export function setLastCopiedLocation ( location : Coordinate ) {
125+ stashedCoordinates = location ;
126+ }
127+
128+ /**
129+ * Paste a pasteable element into the given workspace.
130+ *
131+ * This function does not perform any checks to ensure the paste
132+ * is allowed, e.g. that the workspace is rendered or the block
133+ * is pasteable. Such checks should be done before calling this
134+ * function.
33135 *
34136 * @param copyData The data to paste into the workspace.
35137 * @param workspace The workspace to paste the data into.
@@ -43,7 +145,7 @@ export function paste<T extends ICopyData>(
43145) : ICopyable < T > | null ;
44146
45147/**
46- * Pastes the last copied ICopyable into the workspace.
148+ * Pastes the last copied ICopyable into the last copied-from workspace.
47149 *
48150 * @returns the pasted thing if the paste was successful, null otherwise.
49151 */
@@ -65,7 +167,7 @@ export function paste<T extends ICopyData>(
65167) : ICopyable < ICopyData > | null {
66168 if ( ! copyData || ! workspace ) {
67169 if ( ! stashedCopyData || ! stashedWorkspace ) return null ;
68- return pasteFromData ( stashedCopyData , stashedWorkspace ) ;
170+ return pasteFromData ( stashedCopyData , stashedWorkspace , stashedCoordinates ) ;
69171 }
70172 return pasteFromData ( copyData , workspace , coordinate ) ;
71173}
@@ -85,31 +187,11 @@ function pasteFromData<T extends ICopyData>(
85187) : ICopyable < T > | null {
86188 workspace = workspace . isMutator
87189 ? workspace
88- : ( workspace . getRootWorkspace ( ) ?? workspace ) ;
190+ : // Use the parent workspace if it exists (e.g. for pasting into flyouts)
191+ ( workspace . options . parentWorkspace ?? workspace ) ;
89192 return ( globalRegistry
90193 . getObject ( globalRegistry . Type . PASTER , copyData . paster , false )
91194 ?. paste ( copyData , workspace , coordinate ) ?? null ) as ICopyable < T > | null ;
92195}
93196
94- /**
95- * Private version of duplicate for stubbing in tests.
96- */
97- function duplicateInternal <
98- U extends ICopyData ,
99- T extends ICopyable < U > & IHasWorkspace ,
100- > ( toDuplicate : T ) : T | null {
101- const data = toDuplicate . toCopyData ( ) ;
102- if ( ! data ) return null ;
103- return paste ( data , toDuplicate . workspace ) as T ;
104- }
105-
106- interface IHasWorkspace {
107- workspace : WorkspaceSvg ;
108- }
109-
110- export const TEST_ONLY = {
111- duplicateInternal,
112- copyInternal,
113- } ;
114-
115197export { BlockCopyData , BlockPaster , registry } ;
0 commit comments