expo-clipboard 4.2.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,24 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 4.4.0 — 2023-08-02
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 4.3.0 — 2023-06-21
18
+
19
+ ### 📚 3rd party library updates
20
+
21
+ - Updated `robolectric` to `4.10`. ([#22395](https://github.com/expo/expo/pull/22395) by [@josephyanks](https://github.com/josephyanks))
22
+
23
+ ### 🎉 New features
24
+
25
+ - On iOS, added native `ClipboardPasteButton` view that uses `UIPasteControl`. ([#22823](https://github.com/expo/expo/pull/22823) by [@alanjhughes](https://github.com/alanjhughes))
26
+
27
+ ### 🐛 Bug fixes
28
+
29
+ - Fixed Android build warnings for Gradle version 8. ([#22537](https://github.com/expo/expo/pull/22537), [#22609](https://github.com/expo/expo/pull/22609) by [@kudo](https://github.com/kudo))
30
+
13
31
  ## 4.2.0 — 2023-05-08
14
32
 
15
33
  _This version does not introduce any user-facing changes._
package/README.md CHANGED
@@ -1,4 +1,11 @@
1
- # expo-clipboard
1
+ <p>
2
+ <a href="https://docs.expo.dev/versions/latest/sdk/clipboard/">
3
+ <img
4
+ src="../../.github/resources/expo-clipboard.svg"
5
+ alt="expo-clipboard"
6
+ height="64" />
7
+ </a>
8
+ </p>
2
9
 
3
10
  `expo-clipboard` provides an interface for getting and setting Clipboard content on Android, iOS, and Web.
4
11
 
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '4.2.0'
6
+ version = '4.4.0'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -35,19 +35,11 @@ buildscript {
35
35
  }
36
36
  }
37
37
 
38
- // Creating sources with comments
39
- task androidSourcesJar(type: Jar) {
40
- classifier = 'sources'
41
- from android.sourceSets.main.java.srcDirs
42
- }
43
-
44
38
  afterEvaluate {
45
39
  publishing {
46
40
  publications {
47
41
  release(MavenPublication) {
48
42
  from components.release
49
- // Add additional sourcesJar to artifacts
50
- artifact(androidSourcesJar)
51
43
  }
52
44
  }
53
45
  repositories {
@@ -70,15 +62,21 @@ android {
70
62
  jvmTarget = JavaVersion.VERSION_11.majorVersion
71
63
  }
72
64
 
65
+ namespace "expo.modules.clipboard"
73
66
  defaultConfig {
74
67
  minSdkVersion safeExtGet("minSdkVersion", 21)
75
68
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
76
69
  versionCode 3
77
- versionName '4.2.0'
70
+ versionName '4.4.0'
78
71
  }
79
72
  lintOptions {
80
73
  abortOnError false
81
74
  }
75
+ publishing {
76
+ singleVariant("release") {
77
+ withSourcesJar()
78
+ }
79
+ }
82
80
  }
83
81
 
84
82
  repositories {
@@ -1,5 +1,4 @@
1
- <manifest package="expo.modules.clipboard"
2
- xmlns:android="http://schemas.android.com/apk/res/android">
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
2
  <application>
4
3
  <provider
5
4
  android:name=".ClipboardFileProvider"
@@ -1,5 +1,6 @@
1
1
  import { Subscription } from 'expo-modules-core';
2
2
  import { ClipboardImage, ContentType, GetImageOptions, GetStringOptions, SetStringOptions } from './Clipboard.types';
3
+ import { ClipboardPasteButton } from './ClipboardPasteButton';
3
4
  type ClipboardEvent = {
4
5
  /**
5
6
  * @deprecated Returns empty string. Use [`getStringAsync()`](#getstringasyncoptions) instead to retrieve clipboard content.
@@ -144,5 +145,14 @@ export declare function addClipboardListener(listener: (event: ClipboardEvent) =
144
145
  * ```
145
146
  */
146
147
  export declare function removeClipboardListener(subscription: Subscription): void;
148
+ /**
149
+ * Property that determines if the `ClipboardPasteButton` is available.
150
+ *
151
+ * This requires the users device to be using at least iOS 16.
152
+ *
153
+ * `true` if the component is available, and `false` otherwise.
154
+ */
155
+ export declare const isPasteButtonAvailable: boolean;
147
156
  export * from './Clipboard.types';
157
+ export { ClipboardPasteButton };
148
158
  //# sourceMappingURL=Clipboard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Clipboard.d.ts","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAiC,MAAM,mBAAmB,CAAC;AAE9F,OAAO,EACL,cAAc,EACd,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAO3B,KAAK,cAAc,GAAG;IACpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;AAExC;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAKpF;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQ5C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAKjD;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK1D;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5D;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAKpD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAK5F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtE;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAKtD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,YAAY,CAe5F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,YAAY,QAEjE;AAED,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"Clipboard.d.ts","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAiC,MAAM,mBAAmB,CAAC;AAE9F,OAAO,EACL,cAAc,EACd,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAO9D,KAAK,cAAc,GAAG;IACpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;AAExC;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAKpF;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQ5C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAKjD;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK1D;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5D;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAKpD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAK5F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtE;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAKtD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,YAAY,CAe5F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,YAAY,QAEjE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,OACiC,CAAC;AAEvE,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter, UnavailabilityError, Platform } from 'expo-modules-core';
2
+ import { ClipboardPasteButton } from './ClipboardPasteButton';
2
3
  import ExpoClipboard from './ExpoClipboard';
3
4
  const emitter = new EventEmitter(ExpoClipboard);
4
5
  const onClipboardEventName = 'onClipboardChanged';
@@ -204,5 +205,14 @@ export function addClipboardListener(listener) {
204
205
  export function removeClipboardListener(subscription) {
205
206
  emitter.removeSubscription(subscription);
206
207
  }
208
+ /**
209
+ * Property that determines if the `ClipboardPasteButton` is available.
210
+ *
211
+ * This requires the users device to be using at least iOS 16.
212
+ *
213
+ * `true` if the component is available, and `false` otherwise.
214
+ */
215
+ export const isPasteButtonAvailable = Platform.OS === 'ios' ? ExpoClipboard.isPasteButtonAvailable : false;
207
216
  export * from './Clipboard.types';
217
+ export { ClipboardPasteButton };
208
218
  //# sourceMappingURL=Clipboard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError, Platform } from 'expo-modules-core';\n\nimport {\n ClipboardImage,\n ContentType,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\nimport ExpoClipboard from './ExpoClipboard';\n\nconst emitter = new EventEmitter(ExpoClipboard);\n\nconst onClipboardEventName = 'onClipboardChanged';\n\ntype ClipboardEvent = {\n /**\n * @deprecated Returns empty string. Use [`getStringAsync()`](#getstringasyncoptions) instead to retrieve clipboard content.\n */\n content: string;\n /**\n * An array of content types that are available on the clipboard.\n */\n contentTypes: ContentType[];\n};\n\nexport { Subscription, ClipboardEvent };\n\n/**\n * Gets the content of the user's clipboard. Please note that calling this method on web will prompt\n * the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options Options for the clipboard content to be retrieved.\n * @returns A promise that resolves to the content of the clipboard.\n */\nexport async function getStringAsync(options: GetStringOptions = {}): Promise<string> {\n if (!ExpoClipboard.getStringAsync) {\n throw new UnavailabilityError('Clipboard', 'getStringAsync');\n }\n return await ExpoClipboard.getStringAsync(options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n *\n * @param text The string to save to the clipboard.\n * @param options Options for the clipboard content to be set.\n * @returns On web, this returns a promise that fulfills to a boolean value indicating whether or not\n * the string was saved to the user's clipboard. On iOS and Android, the promise always resolves to `true`.\n */\nexport async function setStringAsync(\n text: string,\n options: SetStringOptions = {}\n): Promise<boolean> {\n if (!ExpoClipboard.setStringAsync) {\n throw new UnavailabilityError('Clipboard', 'setStringAsync');\n }\n return ExpoClipboard.setStringAsync(text, options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n * @deprecated Use [`setStringAsync()`](#setstringasynctext-options) instead.\n *\n * @returns On web, this returns a boolean value indicating whether or not the string was saved to\n * the user's clipboard. On iOS and Android, nothing is returned.\n */\nexport function setString(text: string): void {\n if (Platform.OS === 'web') {\n // on web, we need to return legacy method,\n // because of different return type\n return ExpoClipboard.setString(text);\n } else {\n setStringAsync(text);\n }\n}\n\n/**\n * Returns whether the clipboard has text content. Returns true for both plain text and rich text (e.g. HTML).\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has text content, resolves to `false` otherwise.\n */\nexport function hasStringAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasStringAsync) {\n throw new UnavailabilityError('Clipboard', 'hasStringAsync');\n }\n return ExpoClipboard.hasStringAsync();\n}\n\n/**\n * Gets the URL from the user's clipboard.\n *\n * @returns A promise that fulfills to the URL in the clipboard.\n * @platform ios\n */\nexport async function getUrlAsync(): Promise<string | null> {\n if (!ExpoClipboard.getUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'getUrlAsync');\n }\n return await ExpoClipboard.getUrlAsync();\n}\n\n/**\n * Sets a URL in the user's clipboard.\n *\n * This function behaves the same as [`setStringAsync()`](#setstringasynctext-options), except that\n * it sets the clipboard content type to be a URL. It lets your app or other apps know that the\n * clipboard contains a URL and behave accordingly.\n *\n * @param url The URL to save to the clipboard.\n * @platform ios\n */\nexport async function setUrlAsync(url: string): Promise<void> {\n if (!ExpoClipboard.setUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'setUrlAsync');\n }\n return ExpoClipboard.setUrlAsync(url);\n}\n\n/**\n * Returns whether the clipboard has a URL content.\n *\n * @returns A promise that fulfills to `true` if clipboard has URL content, resolves to `false` otherwise.\n * @platform ios\n */\nexport async function hasUrlAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'hasUrlAsync');\n }\n return await ExpoClipboard.hasUrlAsync();\n}\n\n/**\n * Gets the image from the user's clipboard and returns it in the specified format. Please note that calling\n * this method on web will prompt the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options A `GetImageOptions` object to specify the desired format of the image.\n * @returns If there was an image in the clipboard, the promise resolves to\n * a [`ClipboardImage`](#clipboardimage) object containing the base64 string and metadata of the image.\n * Otherwise, it resolves to `null`.\n *\n * @example\n * ```tsx\n * const img = await Clipboard.getImageAsync({ format: 'png' });\n * // ...\n * <Image source={{ uri: img?.data }} style={{ width: 200, height: 200 }} />\n * ```\n */\nexport async function getImageAsync(options: GetImageOptions): Promise<ClipboardImage | null> {\n if (!ExpoClipboard.getImageAsync) {\n throw new UnavailabilityError('Clipboard', 'getImageAsync');\n }\n return await ExpoClipboard.getImageAsync(options);\n}\n\n/**\n * Sets an image in the user's clipboard.\n *\n * @param base64Image Image encoded as a base64 string, without MIME type.\n *\n * @example\n * ```tsx\n * const result = await ImagePicker.launchImageLibraryAsync({\n * mediaTypes: ImagePicker.MediaTypeOptions.Images,\n * base64: true,\n * });\n * await Clipboard.setImageAsync(result.base64);\n * ```\n */\nexport async function setImageAsync(base64Image: string): Promise<void> {\n if (!ExpoClipboard.setImageAsync) {\n throw new UnavailabilityError('Clipboard', 'setImageAsync');\n }\n return ExpoClipboard.setImageAsync(base64Image);\n}\n\n/**\n * Returns whether the clipboard has an image content.\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has image content, resolves to `false` otherwise.\n */\nexport async function hasImageAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasImageAsync) {\n throw new UnavailabilityError('Clipboard', 'hasImageAsync');\n }\n return ExpoClipboard.hasImageAsync();\n}\n\n/**\n * Adds a listener that will fire whenever the content of the user's clipboard changes. This method\n * is a no-op on Web.\n *\n * @param listener Callback to execute when listener is triggered. The callback is provided a\n * single argument that is an object containing information about clipboard contents.\n *\n * @example\n * ```typescript\n * Clipboard.addClipboardListener(({ contentTypes }: ClipboardEvent) => {\n * if (contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT)) {\n * Clipboard.getStringAsync().then(content => {\n * alert('Copy pasta! Here\\'s the string that was copied: ' + content)\n * });\n * } else if (contentTypes.includes(Clipboard.ContentType.IMAGE)) {\n * alert('Yay! Clipboard contains an image');\n * }\n * });\n * ```\n */\nexport function addClipboardListener(listener: (event: ClipboardEvent) => void): Subscription {\n // TODO: Get rid of this wrapper once we remove deprecated `content` property (not before SDK47)\n const listenerWrapper = (event: ClipboardEvent) => {\n const wrappedEvent: ClipboardEvent = {\n ...event,\n get content(): string {\n console.warn(\n \"The 'content' property of the clipboard event is deprecated. Use 'getStringAsync()' instead to get clipboard content\"\n );\n return '';\n },\n };\n listener(wrappedEvent);\n };\n return emitter.addListener<ClipboardEvent>(onClipboardEventName, listenerWrapper);\n}\n\n/**\n * Removes the listener added by addClipboardListener. This method is a no-op on Web.\n *\n * @param subscription The subscription to remove (created by addClipboardListener).\n *\n * @example\n * ```typescript\n * const subscription = addClipboardListener(() => {\n * alert('Copy pasta!');\n * });\n * removeClipboardListener(subscription);\n * ```\n */\nexport function removeClipboardListener(subscription: Subscription) {\n emitter.removeSubscription(subscription);\n}\n\nexport * from './Clipboard.types';\n"]}
1
+ {"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError, Platform } from 'expo-modules-core';\n\nimport {\n ClipboardImage,\n ContentType,\n GetImageOptions,\n GetStringOptions,\n SetStringOptions,\n} from './Clipboard.types';\nimport { ClipboardPasteButton } from './ClipboardPasteButton';\nimport ExpoClipboard from './ExpoClipboard';\n\nconst emitter = new EventEmitter(ExpoClipboard);\n\nconst onClipboardEventName = 'onClipboardChanged';\n\ntype ClipboardEvent = {\n /**\n * @deprecated Returns empty string. Use [`getStringAsync()`](#getstringasyncoptions) instead to retrieve clipboard content.\n */\n content: string;\n /**\n * An array of content types that are available on the clipboard.\n */\n contentTypes: ContentType[];\n};\n\nexport { Subscription, ClipboardEvent };\n\n/**\n * Gets the content of the user's clipboard. Please note that calling this method on web will prompt\n * the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options Options for the clipboard content to be retrieved.\n * @returns A promise that resolves to the content of the clipboard.\n */\nexport async function getStringAsync(options: GetStringOptions = {}): Promise<string> {\n if (!ExpoClipboard.getStringAsync) {\n throw new UnavailabilityError('Clipboard', 'getStringAsync');\n }\n return await ExpoClipboard.getStringAsync(options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n *\n * @param text The string to save to the clipboard.\n * @param options Options for the clipboard content to be set.\n * @returns On web, this returns a promise that fulfills to a boolean value indicating whether or not\n * the string was saved to the user's clipboard. On iOS and Android, the promise always resolves to `true`.\n */\nexport async function setStringAsync(\n text: string,\n options: SetStringOptions = {}\n): Promise<boolean> {\n if (!ExpoClipboard.setStringAsync) {\n throw new UnavailabilityError('Clipboard', 'setStringAsync');\n }\n return ExpoClipboard.setStringAsync(text, options);\n}\n\n/**\n * Sets the content of the user's clipboard.\n * @deprecated Use [`setStringAsync()`](#setstringasynctext-options) instead.\n *\n * @returns On web, this returns a boolean value indicating whether or not the string was saved to\n * the user's clipboard. On iOS and Android, nothing is returned.\n */\nexport function setString(text: string): void {\n if (Platform.OS === 'web') {\n // on web, we need to return legacy method,\n // because of different return type\n return ExpoClipboard.setString(text);\n } else {\n setStringAsync(text);\n }\n}\n\n/**\n * Returns whether the clipboard has text content. Returns true for both plain text and rich text (e.g. HTML).\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has text content, resolves to `false` otherwise.\n */\nexport function hasStringAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasStringAsync) {\n throw new UnavailabilityError('Clipboard', 'hasStringAsync');\n }\n return ExpoClipboard.hasStringAsync();\n}\n\n/**\n * Gets the URL from the user's clipboard.\n *\n * @returns A promise that fulfills to the URL in the clipboard.\n * @platform ios\n */\nexport async function getUrlAsync(): Promise<string | null> {\n if (!ExpoClipboard.getUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'getUrlAsync');\n }\n return await ExpoClipboard.getUrlAsync();\n}\n\n/**\n * Sets a URL in the user's clipboard.\n *\n * This function behaves the same as [`setStringAsync()`](#setstringasynctext-options), except that\n * it sets the clipboard content type to be a URL. It lets your app or other apps know that the\n * clipboard contains a URL and behave accordingly.\n *\n * @param url The URL to save to the clipboard.\n * @platform ios\n */\nexport async function setUrlAsync(url: string): Promise<void> {\n if (!ExpoClipboard.setUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'setUrlAsync');\n }\n return ExpoClipboard.setUrlAsync(url);\n}\n\n/**\n * Returns whether the clipboard has a URL content.\n *\n * @returns A promise that fulfills to `true` if clipboard has URL content, resolves to `false` otherwise.\n * @platform ios\n */\nexport async function hasUrlAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasUrlAsync) {\n throw new UnavailabilityError('Clipboard', 'hasUrlAsync');\n }\n return await ExpoClipboard.hasUrlAsync();\n}\n\n/**\n * Gets the image from the user's clipboard and returns it in the specified format. Please note that calling\n * this method on web will prompt the user to grant your app permission to \"see text and images copied to the clipboard.\"\n *\n * @param options A `GetImageOptions` object to specify the desired format of the image.\n * @returns If there was an image in the clipboard, the promise resolves to\n * a [`ClipboardImage`](#clipboardimage) object containing the base64 string and metadata of the image.\n * Otherwise, it resolves to `null`.\n *\n * @example\n * ```tsx\n * const img = await Clipboard.getImageAsync({ format: 'png' });\n * // ...\n * <Image source={{ uri: img?.data }} style={{ width: 200, height: 200 }} />\n * ```\n */\nexport async function getImageAsync(options: GetImageOptions): Promise<ClipboardImage | null> {\n if (!ExpoClipboard.getImageAsync) {\n throw new UnavailabilityError('Clipboard', 'getImageAsync');\n }\n return await ExpoClipboard.getImageAsync(options);\n}\n\n/**\n * Sets an image in the user's clipboard.\n *\n * @param base64Image Image encoded as a base64 string, without MIME type.\n *\n * @example\n * ```tsx\n * const result = await ImagePicker.launchImageLibraryAsync({\n * mediaTypes: ImagePicker.MediaTypeOptions.Images,\n * base64: true,\n * });\n * await Clipboard.setImageAsync(result.base64);\n * ```\n */\nexport async function setImageAsync(base64Image: string): Promise<void> {\n if (!ExpoClipboard.setImageAsync) {\n throw new UnavailabilityError('Clipboard', 'setImageAsync');\n }\n return ExpoClipboard.setImageAsync(base64Image);\n}\n\n/**\n * Returns whether the clipboard has an image content.\n *\n * On web, this requires the user to grant your app permission to _\"see text and images copied to the clipboard\"_.\n *\n * @returns A promise that fulfills to `true` if clipboard has image content, resolves to `false` otherwise.\n */\nexport async function hasImageAsync(): Promise<boolean> {\n if (!ExpoClipboard.hasImageAsync) {\n throw new UnavailabilityError('Clipboard', 'hasImageAsync');\n }\n return ExpoClipboard.hasImageAsync();\n}\n\n/**\n * Adds a listener that will fire whenever the content of the user's clipboard changes. This method\n * is a no-op on Web.\n *\n * @param listener Callback to execute when listener is triggered. The callback is provided a\n * single argument that is an object containing information about clipboard contents.\n *\n * @example\n * ```typescript\n * Clipboard.addClipboardListener(({ contentTypes }: ClipboardEvent) => {\n * if (contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT)) {\n * Clipboard.getStringAsync().then(content => {\n * alert('Copy pasta! Here\\'s the string that was copied: ' + content)\n * });\n * } else if (contentTypes.includes(Clipboard.ContentType.IMAGE)) {\n * alert('Yay! Clipboard contains an image');\n * }\n * });\n * ```\n */\nexport function addClipboardListener(listener: (event: ClipboardEvent) => void): Subscription {\n // TODO: Get rid of this wrapper once we remove deprecated `content` property (not before SDK47)\n const listenerWrapper = (event: ClipboardEvent) => {\n const wrappedEvent: ClipboardEvent = {\n ...event,\n get content(): string {\n console.warn(\n \"The 'content' property of the clipboard event is deprecated. Use 'getStringAsync()' instead to get clipboard content\"\n );\n return '';\n },\n };\n listener(wrappedEvent);\n };\n return emitter.addListener<ClipboardEvent>(onClipboardEventName, listenerWrapper);\n}\n\n/**\n * Removes the listener added by addClipboardListener. This method is a no-op on Web.\n *\n * @param subscription The subscription to remove (created by addClipboardListener).\n *\n * @example\n * ```typescript\n * const subscription = addClipboardListener(() => {\n * alert('Copy pasta!');\n * });\n * removeClipboardListener(subscription);\n * ```\n */\nexport function removeClipboardListener(subscription: Subscription) {\n emitter.removeSubscription(subscription);\n}\n\n/**\n * Property that determines if the `ClipboardPasteButton` is available.\n *\n * This requires the users device to be using at least iOS 16.\n *\n * `true` if the component is available, and `false` otherwise.\n */\nexport const isPasteButtonAvailable: boolean =\n Platform.OS === 'ios' ? ExpoClipboard.isPasteButtonAvailable : false;\n\nexport * from './Clipboard.types';\nexport { ClipboardPasteButton };\n"]}
@@ -1,3 +1,4 @@
1
+ import { ViewProps, StyleProp, ViewStyle } from 'react-native';
1
2
  export interface GetImageOptions {
2
3
  /**
3
4
  * The format of the clipboard image to be converted to.
@@ -71,4 +72,75 @@ export interface SetStringOptions {
71
72
  */
72
73
  inputFormat?: StringFormat;
73
74
  }
75
+ export interface ClipboardPasteButtonProps extends ViewProps {
76
+ /**
77
+ * A callback that is called with the result of the paste action.
78
+ * Inspect the `type` property to determine the type of the pasted data.
79
+
80
+ * Can be one of `text` or `image`.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * onPress={(data) => {
85
+ * if (data.type === 'image') {
86
+ * setImageData(data);
87
+ * } else {
88
+ * setTextData(data);
89
+ * }
90
+ * }}
91
+ * ```
92
+ */
93
+ onPress: (data: PasteEventPayload) => void;
94
+ /**
95
+ * The backgroundColor of the button.
96
+ * Leaving this as the default allows the color to adjust to the system theme settings.
97
+ */
98
+ backgroundColor?: string | null;
99
+ /**
100
+ * The foregroundColor of the button.
101
+ * @default white
102
+ */
103
+ foregroundColor?: string | null;
104
+ /**
105
+ * The cornerStyle of the button.
106
+ * @default capsule
107
+ *
108
+ * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uibutton/configuration/cornerstyle) for more details.
109
+ */
110
+ cornerStyle?: CornerStyle | null;
111
+ /**
112
+ * The displayMode of the button.
113
+ * @default `iconAndLabel`
114
+ *
115
+ * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uipastecontrol/displaymode) for more details.
116
+ */
117
+ displayMode?: DisplayMode | null;
118
+ /**
119
+ * The custom style to apply to the button. Should not include `backgroundColor`, `borderRadius` or `color`
120
+ * properties.
121
+ */
122
+ style?: StyleProp<Omit<ViewStyle, 'backgroundColor' | 'borderRadius' | 'color'>>;
123
+ /**
124
+ * The options to use when pasting an image from the clipboard.
125
+ */
126
+ imageOptions?: GetImageOptions | null;
127
+ /**
128
+ * An array of the content types that will cause the button to become active
129
+ * @note do not include `plain-text` and `html` at the same time as this will cause all text to be treated as `html`
130
+ * @default ['plain-text', 'image']
131
+ */
132
+ acceptedContentTypes?: AcceptedContentType[];
133
+ }
134
+ type AcceptedContentType = 'plain-text' | 'image' | 'url' | 'html';
135
+ type CornerStyle = 'dynamic' | 'fixed' | 'capsule' | 'large' | 'medium' | 'small';
136
+ type DisplayMode = 'iconAndLabel' | 'iconOnly' | 'labelOnly';
137
+ export type PasteEventPayload = TextPasteEvent | ImagePasteEvent;
138
+ export interface TextPasteEvent {
139
+ text: string;
140
+ type: 'text';
141
+ }
142
+ export interface ImagePasteEvent extends ClipboardImage {
143
+ type: 'image';
144
+ }
145
+ export {};
74
146
  //# sourceMappingURL=Clipboard.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Clipboard.types.d.ts","sourceRoot":"","sources":["../src/Clipboard.types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;;;OAcG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,oBAAY,WAAW;IACrB,UAAU,eAAe;IACzB,IAAI,SAAS;IACb,KAAK,UAAU;IACf;;OAEG;IACH,GAAG,QAAQ;CACZ;AAED;;GAEG;AACH,oBAAY,YAAY;IACtB,UAAU,cAAc;IACxB,IAAI,SAAS;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,YAAY,CAAC;CAC5B"}
1
+ {"version":3,"file":"Clipboard.types.d.ts","sourceRoot":"","sources":["../src/Clipboard.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG/D,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;;;OAcG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,oBAAY,WAAW;IACrB,UAAU,eAAe;IACzB,IAAI,SAAS;IACb,KAAK,UAAU;IACf;;OAEG;IACH,GAAG,QAAQ;CACZ;AAED;;GAEG;AACH,oBAAY,YAAY;IACtB,UAAU,cAAc;IACxB,IAAI,SAAS;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,YAAY,CAAC;CAC5B;AAGD,MAAM,WAAW,yBAA0B,SAAQ,SAAS;IAC1D;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,GAAG,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IACjF;;OAEG;IACH,YAAY,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC9C;AAED,KAAK,mBAAmB,GAAG,YAAY,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEnE,KAAK,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElF,KAAK,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,eAAe,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,IAAI,EAAE,OAAO,CAAC;CACf"}
@@ -1 +1 @@
1
- {"version":3,"file":"Clipboard.types.js","sourceRoot":"","sources":["../src/Clipboard.types.ts"],"names":[],"mappings":"AAyCA;;GAEG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,wCAAyB,CAAA;IACzB,4BAAa,CAAA;IACb,8BAAe,CAAA;IACf;;OAEG;IACH,0BAAW,CAAA;AACb,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,wCAAwB,CAAA;IACxB,6BAAa,CAAA;AACf,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB","sourcesContent":["// @needsAudit\nexport interface GetImageOptions {\n /**\n * The format of the clipboard image to be converted to.\n */\n format: 'png' | 'jpeg';\n /**\n * Specify the quality of the returned image, between `0` and `1`. Defaults to `1` (highest quality).\n * Applicable only when `format` is set to `jpeg`, ignored otherwise.\n * @default 1\n */\n jpegQuality?: number;\n}\n\n// @needsAudit\nexport interface ClipboardImage {\n /**\n * A Base64-encoded string of the image data.\n * Its format is dependent on the `format` option.\n *\n * > **NOTE:** The string is already prepended with `data:image/png;base64,` or `data:image/jpeg;base64,` prefix.\n *\n * You can use it directly as the source of an `Image` element.\n * @example\n * ```ts\n * <Image\n * source={{ uri: clipboardImage.data }}\n * style={{ width: 200, height: 200 }}\n * />\n * ```\n */\n data: string;\n /**\n * Dimensions (`width` and `height`) of the image pasted from clipboard.\n */\n size: {\n width: number;\n height: number;\n };\n}\n\n/**\n * Type used to define what type of data is stored in the clipboard.\n */\nexport enum ContentType {\n PLAIN_TEXT = 'plain-text',\n HTML = 'html',\n IMAGE = 'image',\n /**\n * @platform iOS\n */\n URL = 'url',\n}\n\n/**\n * Type used to determine string format stored in the clipboard.\n */\nexport enum StringFormat {\n PLAIN_TEXT = 'plainText',\n HTML = 'html',\n}\n\nexport interface GetStringOptions {\n /**\n * The target format of the clipboard string to be converted to, if possible.\n *\n * @default StringFormat.PLAIN_TEXT\n */\n preferredFormat?: StringFormat;\n}\n\nexport interface SetStringOptions {\n /**\n * The input format of the provided string.\n * Adjusting this option can help other applications interpret copied string properly.\n *\n * @default StringFormat.PLAIN_TEXT\n */\n inputFormat?: StringFormat;\n}\n"]}
1
+ {"version":3,"file":"Clipboard.types.js","sourceRoot":"","sources":["../src/Clipboard.types.ts"],"names":[],"mappings":"AA2CA;;GAEG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,wCAAyB,CAAA;IACzB,4BAAa,CAAA;IACb,8BAAe,CAAA;IACf;;OAEG;IACH,0BAAW,CAAA;AACb,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,wCAAwB,CAAA;IACxB,6BAAa,CAAA;AACf,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB","sourcesContent":["import { ViewProps, StyleProp, ViewStyle } from 'react-native';\n\n// @needsAudit\nexport interface GetImageOptions {\n /**\n * The format of the clipboard image to be converted to.\n */\n format: 'png' | 'jpeg';\n /**\n * Specify the quality of the returned image, between `0` and `1`. Defaults to `1` (highest quality).\n * Applicable only when `format` is set to `jpeg`, ignored otherwise.\n * @default 1\n */\n jpegQuality?: number;\n}\n\n// @needsAudit\nexport interface ClipboardImage {\n /**\n * A Base64-encoded string of the image data.\n * Its format is dependent on the `format` option.\n *\n * > **NOTE:** The string is already prepended with `data:image/png;base64,` or `data:image/jpeg;base64,` prefix.\n *\n * You can use it directly as the source of an `Image` element.\n * @example\n * ```ts\n * <Image\n * source={{ uri: clipboardImage.data }}\n * style={{ width: 200, height: 200 }}\n * />\n * ```\n */\n data: string;\n /**\n * Dimensions (`width` and `height`) of the image pasted from clipboard.\n */\n size: {\n width: number;\n height: number;\n };\n}\n\n/**\n * Type used to define what type of data is stored in the clipboard.\n */\nexport enum ContentType {\n PLAIN_TEXT = 'plain-text',\n HTML = 'html',\n IMAGE = 'image',\n /**\n * @platform iOS\n */\n URL = 'url',\n}\n\n/**\n * Type used to determine string format stored in the clipboard.\n */\nexport enum StringFormat {\n PLAIN_TEXT = 'plainText',\n HTML = 'html',\n}\n\nexport interface GetStringOptions {\n /**\n * The target format of the clipboard string to be converted to, if possible.\n *\n * @default StringFormat.PLAIN_TEXT\n */\n preferredFormat?: StringFormat;\n}\n\nexport interface SetStringOptions {\n /**\n * The input format of the provided string.\n * Adjusting this option can help other applications interpret copied string properly.\n *\n * @default StringFormat.PLAIN_TEXT\n */\n inputFormat?: StringFormat;\n}\n\n// @needsAudit\nexport interface ClipboardPasteButtonProps extends ViewProps {\n /**\n * A callback that is called with the result of the paste action.\n * Inspect the `type` property to determine the type of the pasted data.\n \n * Can be one of `text` or `image`.\n *\n * @example\n * ```ts\n * onPress={(data) => {\n * if (data.type === 'image') {\n * setImageData(data);\n * } else {\n * setTextData(data);\n * }\n * }}\n * ```\n */\n onPress: (data: PasteEventPayload) => void;\n /**\n * The backgroundColor of the button.\n * Leaving this as the default allows the color to adjust to the system theme settings.\n */\n backgroundColor?: string | null;\n /**\n * The foregroundColor of the button.\n * @default white\n */\n foregroundColor?: string | null;\n /**\n * The cornerStyle of the button.\n * @default capsule\n *\n * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uibutton/configuration/cornerstyle) for more details.\n */\n cornerStyle?: CornerStyle | null;\n /**\n * The displayMode of the button.\n * @default `iconAndLabel`\n *\n * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uipastecontrol/displaymode) for more details.\n */\n displayMode?: DisplayMode | null;\n /**\n * The custom style to apply to the button. Should not include `backgroundColor`, `borderRadius` or `color`\n * properties.\n */\n style?: StyleProp<Omit<ViewStyle, 'backgroundColor' | 'borderRadius' | 'color'>>;\n /**\n * The options to use when pasting an image from the clipboard.\n */\n imageOptions?: GetImageOptions | null;\n /**\n * An array of the content types that will cause the button to become active\n * @note do not include `plain-text` and `html` at the same time as this will cause all text to be treated as `html`\n * @default ['plain-text', 'image']\n */\n acceptedContentTypes?: AcceptedContentType[];\n}\n\ntype AcceptedContentType = 'plain-text' | 'image' | 'url' | 'html';\n\ntype CornerStyle = 'dynamic' | 'fixed' | 'capsule' | 'large' | 'medium' | 'small';\n\ntype DisplayMode = 'iconAndLabel' | 'iconOnly' | 'labelOnly';\n\nexport type PasteEventPayload = TextPasteEvent | ImagePasteEvent;\n\nexport interface TextPasteEvent {\n text: string;\n type: 'text';\n}\n\nexport interface ImagePasteEvent extends ClipboardImage {\n type: 'image';\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import { ClipboardPasteButtonProps } from './Clipboard.types';
2
+ /**
3
+ * This component displays the `UIPasteControl` button on your screen. This allows pasting from the clipboard without requesting permission from the user.
4
+ *
5
+ * You should only attempt to render this if [`Clipboard.isPasteButtonAvailable`](#ispastebuttonavailable)
6
+ * is `true`. This component will render nothing if it is not available, and you will get
7
+ * a warning in development mode (`__DEV__ === true`).
8
+ *
9
+ * The properties of this component extend from `View`; however, you should not attempt to set
10
+ * `backgroundColor`, `color` or `borderRadius` with the `style` property. Apple restricts customisation of this view.
11
+ * Instead, you should use the backgroundColor and foregroundColor properties to set the colors of the button, the cornerStyle property to change the border radius,
12
+ * and the displayMode property to change the appearance of the icon and label. The word "Paste" is not editable and neither is the icon.
13
+ *
14
+ * Make sure to attach height and width via the style props as without these styles, the button will
15
+ * not appear on the screen.
16
+ *
17
+ * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uipastecontrol) for more details.
18
+ */
19
+ export declare function ClipboardPasteButton({ onPress, ...restProps }: ClipboardPasteButtonProps): JSX.Element | null;
20
+ //# sourceMappingURL=ClipboardPasteButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClipboardPasteButton.d.ts","sourceRoot":"","sources":["../src/ClipboardPasteButton.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAqB,MAAM,mBAAmB,CAAC;AAIjF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,EAAE,yBAAyB,sBAaxF"}
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import ExpoClipboardPasteButton from './ExpoClipboardPasteButton';
3
+ // @needsAudit
4
+ /**
5
+ * This component displays the `UIPasteControl` button on your screen. This allows pasting from the clipboard without requesting permission from the user.
6
+ *
7
+ * You should only attempt to render this if [`Clipboard.isPasteButtonAvailable`](#ispastebuttonavailable)
8
+ * is `true`. This component will render nothing if it is not available, and you will get
9
+ * a warning in development mode (`__DEV__ === true`).
10
+ *
11
+ * The properties of this component extend from `View`; however, you should not attempt to set
12
+ * `backgroundColor`, `color` or `borderRadius` with the `style` property. Apple restricts customisation of this view.
13
+ * Instead, you should use the backgroundColor and foregroundColor properties to set the colors of the button, the cornerStyle property to change the border radius,
14
+ * and the displayMode property to change the appearance of the icon and label. The word "Paste" is not editable and neither is the icon.
15
+ *
16
+ * Make sure to attach height and width via the style props as without these styles, the button will
17
+ * not appear on the screen.
18
+ *
19
+ * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uipastecontrol) for more details.
20
+ */
21
+ export function ClipboardPasteButton({ onPress, ...restProps }) {
22
+ if (!ExpoClipboardPasteButton) {
23
+ if (__DEV__) {
24
+ console.warn("'ApplePasteButton' is not available.");
25
+ }
26
+ return null;
27
+ }
28
+ const onPastePressed = ({ nativeEvent }) => {
29
+ onPress(nativeEvent);
30
+ };
31
+ return React.createElement(ExpoClipboardPasteButton, { onPastePressed: onPastePressed, ...restProps });
32
+ }
33
+ //# sourceMappingURL=ClipboardPasteButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClipboardPasteButton.js","sourceRoot":"","sources":["../src/ClipboardPasteButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,wBAAwB,MAAM,4BAA4B,CAAC;AAElE,cAAc;AACd;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,EAA6B;IACvF,IAAI,CAAC,wBAAwB,EAAE;QAC7B,IAAI,OAAO,EAAE;YACX,OAAO,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;SACtD;QACD,OAAO,IAAI,CAAC;KACb;IAED,MAAM,cAAc,GAAG,CAAC,EAAE,WAAW,EAA2C,EAAE,EAAE;QAClF,OAAO,CAAC,WAAW,CAAC,CAAC;IACvB,CAAC,CAAC;IAEF,OAAO,oBAAC,wBAAwB,IAAC,cAAc,EAAE,cAAc,KAAM,SAAS,GAAI,CAAC;AACrF,CAAC","sourcesContent":["import React from 'react';\nimport { NativeSyntheticEvent } from 'react-native';\n\nimport { ClipboardPasteButtonProps, PasteEventPayload } from './Clipboard.types';\nimport ExpoClipboardPasteButton from './ExpoClipboardPasteButton';\n\n// @needsAudit\n/**\n * This component displays the `UIPasteControl` button on your screen. This allows pasting from the clipboard without requesting permission from the user.\n *\n * You should only attempt to render this if [`Clipboard.isPasteButtonAvailable`](#ispastebuttonavailable)\n * is `true`. This component will render nothing if it is not available, and you will get\n * a warning in development mode (`__DEV__ === true`).\n *\n * The properties of this component extend from `View`; however, you should not attempt to set\n * `backgroundColor`, `color` or `borderRadius` with the `style` property. Apple restricts customisation of this view.\n * Instead, you should use the backgroundColor and foregroundColor properties to set the colors of the button, the cornerStyle property to change the border radius,\n * and the displayMode property to change the appearance of the icon and label. The word \"Paste\" is not editable and neither is the icon.\n *\n * Make sure to attach height and width via the style props as without these styles, the button will\n * not appear on the screen.\n *\n * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uipastecontrol) for more details.\n */\nexport function ClipboardPasteButton({ onPress, ...restProps }: ClipboardPasteButtonProps) {\n if (!ExpoClipboardPasteButton) {\n if (__DEV__) {\n console.warn(\"'ApplePasteButton' is not available.\");\n }\n return null;\n }\n\n const onPastePressed = ({ nativeEvent }: NativeSyntheticEvent<PasteEventPayload>) => {\n onPress(nativeEvent);\n };\n\n return <ExpoClipboardPasteButton onPastePressed={onPastePressed} {...restProps} />;\n}\n"]}
@@ -0,0 +1,3 @@
1
+ declare let ExpoClipboard: any;
2
+ export default ExpoClipboard;
3
+ //# sourceMappingURL=ExpoClipboardPasteButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoClipboardPasteButton.d.ts","sourceRoot":"","sources":["../src/ExpoClipboardPasteButton.ts"],"names":[],"mappings":"AAGA,QAAA,IAAI,aAAa,EAAE,GAAG,CAAC;AAMvB,eAAe,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { requireNativeViewManager } from 'expo-modules-core';
2
+ import { Platform } from 'react-native';
3
+ let ExpoClipboard;
4
+ if (Platform.OS === 'ios') {
5
+ ExpoClipboard = requireNativeViewManager('ExpoClipboard');
6
+ }
7
+ export default ExpoClipboard;
8
+ //# sourceMappingURL=ExpoClipboardPasteButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoClipboardPasteButton.js","sourceRoot":"","sources":["../src/ExpoClipboardPasteButton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,IAAI,aAAkB,CAAC;AAEvB,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE;IACzB,aAAa,GAAG,wBAAwB,CAAC,eAAe,CAAC,CAAC;CAC3D;AAED,eAAe,aAAa,CAAC","sourcesContent":["import { requireNativeViewManager } from 'expo-modules-core';\nimport { Platform } from 'react-native';\n\nlet ExpoClipboard: any;\n\nif (Platform.OS === 'ios') {\n ExpoClipboard = requireNativeViewManager('ExpoClipboard');\n}\n\nexport default ExpoClipboard;\n"]}
@@ -9,7 +9,7 @@ public class ClipboardModule: Module {
9
9
  public func definition() -> ModuleDefinition {
10
10
  Name("ExpoClipboard")
11
11
 
12
- // MARK: Strings
12
+ // MARK: - Strings
13
13
 
14
14
  AsyncFunction("getStringAsync") { (options: GetStringOptions) -> String in
15
15
  switch options.preferredFormat {
@@ -35,7 +35,7 @@ public class ClipboardModule: Module {
35
35
  return UIPasteboard.general.hasStrings || UIPasteboard.general.hasHTML
36
36
  }
37
37
 
38
- // MARK: URLs
38
+ // MARK: - URLs
39
39
 
40
40
  AsyncFunction("getUrlAsync") { () -> String? in
41
41
  return UIPasteboard.general.url?.absoluteString
@@ -49,7 +49,7 @@ public class ClipboardModule: Module {
49
49
  return UIPasteboard.general.hasURLs
50
50
  }
51
51
 
52
- // MARK: Images
52
+ // MARK: - Images
53
53
 
54
54
  AsyncFunction("setImageAsync") { (content: String) in
55
55
  guard let data = Data(base64Encoded: content),
@@ -82,7 +82,14 @@ public class ClipboardModule: Module {
82
82
  ]
83
83
  }
84
84
 
85
- // MARK: Events
85
+ Property("isPasteButtonAvailable") { () -> Bool in
86
+ if #available(iOS 16.0, *) {
87
+ return true
88
+ }
89
+ return false
90
+ }
91
+
92
+ // MARK: - Events
86
93
 
87
94
  Events(onClipboardChanged)
88
95
 
@@ -99,6 +106,44 @@ public class ClipboardModule: Module {
99
106
  OnStopObserving {
100
107
  NotificationCenter.default.removeObserver(self, name: UIPasteboard.changedNotification, object: nil)
101
108
  }
109
+
110
+ // MARK: - View
111
+
112
+ View(ClipboardPasteButton.self) {
113
+ Events("onPastePressed")
114
+
115
+ Prop("backgroundColor") { (view, color: UIColor?) in
116
+ if view.baseBackgroundColor != color {
117
+ view.baseBackgroundColor = color
118
+ }
119
+ }
120
+
121
+ Prop("foregroundColor") { (view, color: UIColor?) in
122
+ if view.baseForegroundColor != color {
123
+ view.baseForegroundColor = color
124
+ }
125
+ }
126
+
127
+ Prop("acceptedContentTypes") { (view, types: [AcceptedTypes]?) in
128
+ view.acceptedContentTypes = types ?? []
129
+ }
130
+
131
+ Prop("cornerStyle") { (view, style: CornerStyle?) in
132
+ view.cornerStyle = style ?? .capsule
133
+ }
134
+
135
+ Prop("displayMode") { (view, mode: DisplayMode?) in
136
+ view.displayMode = mode ?? .iconAndLabel
137
+ }
138
+
139
+ Prop("imageOptions") { (view, options: GetImageOptions?) in
140
+ view.imageOptions = options ?? GetImageOptions()
141
+ }
142
+
143
+ OnViewDidUpdateProps { view in
144
+ view.update()
145
+ }
146
+ }
102
147
  }
103
148
 
104
149
  @objc
@@ -0,0 +1,170 @@
1
+ import ExpoModulesCore
2
+ import UniformTypeIdentifiers
3
+
4
+ class ClipboardPasteButton: ExpoView {
5
+ let onPastePressed = EventDispatcher()
6
+
7
+ // MARK: - Properties
8
+ var baseBackgroundColor: UIColor?
9
+ var baseForegroundColor: UIColor?
10
+ var cornerStyle: CornerStyle = .capsule
11
+ var displayMode: DisplayMode = .iconAndLabel
12
+ var acceptedContentTypes: [AcceptedTypes] = []
13
+ var imageOptions = GetImageOptions()
14
+
15
+ private var childView: UIView?
16
+
17
+ func update() {
18
+ unmountChild()
19
+ if #available(iOS 16.0, *) {
20
+ mountView()
21
+ } else {
22
+ log.error("ClipboardPasteButton is only supported on iOS 16 and above")
23
+ }
24
+ }
25
+
26
+ @available(iOS 16.0, *)
27
+ private func mountView() {
28
+ let configuration = UIPasteControl.Configuration()
29
+ configuration.baseBackgroundColor = baseBackgroundColor
30
+ configuration.baseForegroundColor = baseForegroundColor
31
+ configuration.cornerStyle = cornerStyle.toCornerStyle()
32
+ configuration.displayMode = displayMode.toUIDisplayMode()
33
+
34
+ let control = UIPasteControl(configuration: configuration)
35
+ control.translatesAutoresizingMaskIntoConstraints = false
36
+ control.target = self
37
+ setContentTypes()
38
+
39
+ self.addSubview(control)
40
+ childView = control
41
+
42
+ NSLayoutConstraint.activate([
43
+ control.topAnchor.constraint(equalTo: topAnchor),
44
+ control.bottomAnchor.constraint(equalTo: bottomAnchor),
45
+ control.leadingAnchor.constraint(equalTo: leadingAnchor),
46
+ control.trailingAnchor.constraint(equalTo: trailingAnchor)
47
+ ])
48
+ }
49
+
50
+ override func paste(itemProviders: [NSItemProvider]) {
51
+ guard #available(iOS 14.0, *) else {
52
+ return
53
+ }
54
+ for provider in itemProviders {
55
+ if provider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
56
+ _ = provider.loadObject(ofClass: UIImage.self) { data, error in
57
+ guard error == nil else {
58
+ log.error("Error loading pasted image")
59
+ return
60
+ }
61
+ self.processImage(item: data)
62
+ }
63
+ } else if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
64
+ _ = provider.loadObject(ofClass: URL.self) { data, error in
65
+ guard error == nil else {
66
+ log.error("Error loading pasted content")
67
+ return
68
+ }
69
+
70
+ self.onPastePressed([
71
+ "type": "text",
72
+ "text": data?.absoluteString
73
+ ])
74
+ }
75
+ } else if provider.hasItemConformingToTypeIdentifier(UTType.html.identifier) && acceptedContentTypes.contains(.html) {
76
+ _ = provider.loadObject(ofClass: String.self) { data, error in
77
+ guard error == nil else {
78
+ log.error("Error loading pasted content")
79
+ return
80
+ }
81
+ self.processHtml(data: data)
82
+ }
83
+ } else if provider.hasItemConformingToTypeIdentifier(UTType.utf8PlainText.identifier) && !acceptedContentTypes.contains(.html) {
84
+ _ = provider.loadObject(ofClass: String.self) { data, error in
85
+ guard error == nil else {
86
+ log.error("Error loading pasted content")
87
+ return
88
+ }
89
+
90
+ guard let data = data as? String else {
91
+ log.error("Failed to read text data")
92
+ return
93
+ }
94
+
95
+ self.onPastePressed([
96
+ "type": "text",
97
+ "text": data
98
+ ])
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ @available(iOS 14.0, *)
105
+ private func setContentTypes() {
106
+ if acceptedContentTypes.isEmpty {
107
+ pasteConfiguration = UIPasteConfiguration(acceptableTypeIdentifiers: [
108
+ UTType.utf8PlainText.identifier,
109
+ UTType.image.identifier
110
+ ])
111
+ } else {
112
+ pasteConfiguration = UIPasteConfiguration(acceptableTypeIdentifiers: acceptedContentTypes.map {
113
+ $0.typeIdentifier()
114
+ })
115
+ }
116
+ }
117
+
118
+ private func processImage(item: NSItemProviderReading?) {
119
+ guard let image = item as? UIImage else {
120
+ log.error("Failed to read image data")
121
+ return
122
+ }
123
+
124
+ guard let data = imageToData(image) else {
125
+ log.error("Failed to process image data")
126
+ return
127
+ }
128
+
129
+ guard let fileSystem = appContext?.fileSystem else {
130
+ log.error("Failed to access FileSystem")
131
+ return
132
+ }
133
+
134
+ let imageData = "data:\(imageOptions.imageFormat.getMimeType());base64,\(data.base64EncodedString())"
135
+ self.onPastePressed([
136
+ "type": "image",
137
+ "data": imageData,
138
+ "size": [
139
+ "width": image.size.width,
140
+ "height": image.size.height
141
+ ]
142
+ ])
143
+ }
144
+
145
+ private func processHtml(data: String?) {
146
+ guard let htmlString = data as? String else {
147
+ log.error("Failed to read html data")
148
+ return
149
+ }
150
+
151
+ let attributedString = try? NSAttributedString(htmlString: htmlString)
152
+
153
+ self.onPastePressed([
154
+ "type": "text",
155
+ "text": attributedString?.htmlString ?? ""
156
+ ])
157
+ }
158
+
159
+ private func unmountChild() {
160
+ childView?.removeFromSuperview()
161
+ childView = nil
162
+ }
163
+
164
+ private func imageToData(_ image: UIImage) -> Data? {
165
+ switch imageOptions.imageFormat {
166
+ case .jpeg: return image.jpegData(compressionQuality: imageOptions.jpegQuality)
167
+ case .png: return image.pngData()
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,67 @@
1
+ import ExpoModulesCore
2
+
3
+ enum CornerStyle: String, Enumerable {
4
+ case dynamic
5
+ case fixed
6
+ case capsule
7
+ case large
8
+ case medium
9
+ case small
10
+
11
+ @available(iOS 16.0, *)
12
+ func toCornerStyle() -> UIButton.Configuration.CornerStyle {
13
+ switch self {
14
+ case .dynamic:
15
+ return .dynamic
16
+ case .fixed:
17
+ return .fixed
18
+ case .capsule:
19
+ return .capsule
20
+ case .large:
21
+ return .large
22
+ case .medium:
23
+ return .medium
24
+ case .small:
25
+ return .small
26
+ }
27
+ }
28
+ }
29
+
30
+ enum DisplayMode: String, Enumerable {
31
+ case iconAndLabel
32
+ case iconOnly
33
+ case labelOnly
34
+
35
+ @available(iOS 16.0, *)
36
+ func toUIDisplayMode() -> UIPasteControl.DisplayMode {
37
+ switch self {
38
+ case .iconOnly:
39
+ return .iconOnly
40
+ case .labelOnly:
41
+ return .labelOnly
42
+ case .iconAndLabel:
43
+ return .iconAndLabel
44
+ }
45
+ }
46
+ }
47
+
48
+ enum AcceptedTypes: String, Enumerable {
49
+ case plainText = "plain-text"
50
+ case url
51
+ case html
52
+ case image
53
+
54
+ @available(iOS 14.0, *)
55
+ func typeIdentifier() -> String {
56
+ switch self {
57
+ case .plainText:
58
+ return UTType.utf8PlainText.identifier
59
+ case .html:
60
+ return UTType.html.identifier
61
+ case .image:
62
+ return UTType.image.identifier
63
+ case .url:
64
+ return UTType.url.identifier
65
+ }
66
+ }
67
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-clipboard",
3
- "version": "4.2.0",
3
+ "version": "4.4.0",
4
4
  "description": "ExpoClipboard standalone module",
5
5
  "main": "build/Clipboard.js",
6
6
  "types": "build/Clipboard.d.ts",
@@ -39,5 +39,5 @@
39
39
  "jest": {
40
40
  "preset": "expo-module-scripts/universal"
41
41
  },
42
- "gitHead": "4ba50c428c8369bb6b3a51a860d4898ad4ccbe78"
42
+ "gitHead": "2240630a92eb79a4e4bf73e1439916c394876478"
43
43
  }
package/src/Clipboard.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  GetStringOptions,
8
8
  SetStringOptions,
9
9
  } from './Clipboard.types';
10
+ import { ClipboardPasteButton } from './ClipboardPasteButton';
10
11
  import ExpoClipboard from './ExpoClipboard';
11
12
 
12
13
  const emitter = new EventEmitter(ExpoClipboard);
@@ -244,4 +245,15 @@ export function removeClipboardListener(subscription: Subscription) {
244
245
  emitter.removeSubscription(subscription);
245
246
  }
246
247
 
248
+ /**
249
+ * Property that determines if the `ClipboardPasteButton` is available.
250
+ *
251
+ * This requires the users device to be using at least iOS 16.
252
+ *
253
+ * `true` if the component is available, and `false` otherwise.
254
+ */
255
+ export const isPasteButtonAvailable: boolean =
256
+ Platform.OS === 'ios' ? ExpoClipboard.isPasteButtonAvailable : false;
257
+
247
258
  export * from './Clipboard.types';
259
+ export { ClipboardPasteButton };
@@ -1,3 +1,5 @@
1
+ import { ViewProps, StyleProp, ViewStyle } from 'react-native';
2
+
1
3
  // @needsAudit
2
4
  export interface GetImageOptions {
3
5
  /**
@@ -78,3 +80,81 @@ export interface SetStringOptions {
78
80
  */
79
81
  inputFormat?: StringFormat;
80
82
  }
83
+
84
+ // @needsAudit
85
+ export interface ClipboardPasteButtonProps extends ViewProps {
86
+ /**
87
+ * A callback that is called with the result of the paste action.
88
+ * Inspect the `type` property to determine the type of the pasted data.
89
+
90
+ * Can be one of `text` or `image`.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * onPress={(data) => {
95
+ * if (data.type === 'image') {
96
+ * setImageData(data);
97
+ * } else {
98
+ * setTextData(data);
99
+ * }
100
+ * }}
101
+ * ```
102
+ */
103
+ onPress: (data: PasteEventPayload) => void;
104
+ /**
105
+ * The backgroundColor of the button.
106
+ * Leaving this as the default allows the color to adjust to the system theme settings.
107
+ */
108
+ backgroundColor?: string | null;
109
+ /**
110
+ * The foregroundColor of the button.
111
+ * @default white
112
+ */
113
+ foregroundColor?: string | null;
114
+ /**
115
+ * The cornerStyle of the button.
116
+ * @default capsule
117
+ *
118
+ * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uibutton/configuration/cornerstyle) for more details.
119
+ */
120
+ cornerStyle?: CornerStyle | null;
121
+ /**
122
+ * The displayMode of the button.
123
+ * @default `iconAndLabel`
124
+ *
125
+ * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uipastecontrol/displaymode) for more details.
126
+ */
127
+ displayMode?: DisplayMode | null;
128
+ /**
129
+ * The custom style to apply to the button. Should not include `backgroundColor`, `borderRadius` or `color`
130
+ * properties.
131
+ */
132
+ style?: StyleProp<Omit<ViewStyle, 'backgroundColor' | 'borderRadius' | 'color'>>;
133
+ /**
134
+ * The options to use when pasting an image from the clipboard.
135
+ */
136
+ imageOptions?: GetImageOptions | null;
137
+ /**
138
+ * An array of the content types that will cause the button to become active
139
+ * @note do not include `plain-text` and `html` at the same time as this will cause all text to be treated as `html`
140
+ * @default ['plain-text', 'image']
141
+ */
142
+ acceptedContentTypes?: AcceptedContentType[];
143
+ }
144
+
145
+ type AcceptedContentType = 'plain-text' | 'image' | 'url' | 'html';
146
+
147
+ type CornerStyle = 'dynamic' | 'fixed' | 'capsule' | 'large' | 'medium' | 'small';
148
+
149
+ type DisplayMode = 'iconAndLabel' | 'iconOnly' | 'labelOnly';
150
+
151
+ export type PasteEventPayload = TextPasteEvent | ImagePasteEvent;
152
+
153
+ export interface TextPasteEvent {
154
+ text: string;
155
+ type: 'text';
156
+ }
157
+
158
+ export interface ImagePasteEvent extends ClipboardImage {
159
+ type: 'image';
160
+ }
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { NativeSyntheticEvent } from 'react-native';
3
+
4
+ import { ClipboardPasteButtonProps, PasteEventPayload } from './Clipboard.types';
5
+ import ExpoClipboardPasteButton from './ExpoClipboardPasteButton';
6
+
7
+ // @needsAudit
8
+ /**
9
+ * This component displays the `UIPasteControl` button on your screen. This allows pasting from the clipboard without requesting permission from the user.
10
+ *
11
+ * You should only attempt to render this if [`Clipboard.isPasteButtonAvailable`](#ispastebuttonavailable)
12
+ * is `true`. This component will render nothing if it is not available, and you will get
13
+ * a warning in development mode (`__DEV__ === true`).
14
+ *
15
+ * The properties of this component extend from `View`; however, you should not attempt to set
16
+ * `backgroundColor`, `color` or `borderRadius` with the `style` property. Apple restricts customisation of this view.
17
+ * Instead, you should use the backgroundColor and foregroundColor properties to set the colors of the button, the cornerStyle property to change the border radius,
18
+ * and the displayMode property to change the appearance of the icon and label. The word "Paste" is not editable and neither is the icon.
19
+ *
20
+ * Make sure to attach height and width via the style props as without these styles, the button will
21
+ * not appear on the screen.
22
+ *
23
+ * @see [Apple Documentation](https://developer.apple.com/documentation/uikit/uipastecontrol) for more details.
24
+ */
25
+ export function ClipboardPasteButton({ onPress, ...restProps }: ClipboardPasteButtonProps) {
26
+ if (!ExpoClipboardPasteButton) {
27
+ if (__DEV__) {
28
+ console.warn("'ApplePasteButton' is not available.");
29
+ }
30
+ return null;
31
+ }
32
+
33
+ const onPastePressed = ({ nativeEvent }: NativeSyntheticEvent<PasteEventPayload>) => {
34
+ onPress(nativeEvent);
35
+ };
36
+
37
+ return <ExpoClipboardPasteButton onPastePressed={onPastePressed} {...restProps} />;
38
+ }
@@ -0,0 +1,10 @@
1
+ import { requireNativeViewManager } from 'expo-modules-core';
2
+ import { Platform } from 'react-native';
3
+
4
+ let ExpoClipboard: any;
5
+
6
+ if (Platform.OS === 'ios') {
7
+ ExpoClipboard = requireNativeViewManager('ExpoClipboard');
8
+ }
9
+
10
+ export default ExpoClipboard;
package/tsconfig.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "outDir": "./build"
6
6
  },
7
7
  "include": ["./src"],
8
- "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__stories__/*"]
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*"]
9
9
  }