expo-clipboard 4.1.2 → 4.3.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 +19 -1
- package/README.md +12 -7
- package/android/build.gradle +8 -10
- package/android/src/main/AndroidManifest.xml +1 -2
- package/build/Clipboard.d.ts +10 -0
- package/build/Clipboard.d.ts.map +1 -1
- package/build/Clipboard.js +10 -0
- package/build/Clipboard.js.map +1 -1
- package/build/Clipboard.types.d.ts +72 -0
- package/build/Clipboard.types.d.ts.map +1 -1
- package/build/Clipboard.types.js.map +1 -1
- package/build/ClipboardPasteButton.d.ts +20 -0
- package/build/ClipboardPasteButton.d.ts.map +1 -0
- package/build/ClipboardPasteButton.js +33 -0
- package/build/ClipboardPasteButton.js.map +1 -0
- package/build/ExpoClipboardPasteButton.d.ts +3 -0
- package/build/ExpoClipboardPasteButton.d.ts.map +1 -0
- package/build/ExpoClipboardPasteButton.js +8 -0
- package/build/ExpoClipboardPasteButton.js.map +1 -0
- package/ios/ClipboardModule.swift +49 -4
- package/ios/ClipboardPasteButton.swift +170 -0
- package/ios/ClipboardPasteButtonRecords.swift +67 -0
- package/ios/Tests/ClipboardModuleSpec.swift +1 -1
- package/package.json +2 -2
- package/src/Clipboard.ts +12 -0
- package/src/Clipboard.types.ts +80 -0
- package/src/ClipboardPasteButton.tsx +38 -0
- package/src/ExpoClipboardPasteButton.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,7 +10,25 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
-
## 4.
|
|
13
|
+
## 4.3.0 — 2023-06-21
|
|
14
|
+
|
|
15
|
+
### 📚 3rd party library updates
|
|
16
|
+
|
|
17
|
+
- Updated `robolectric` to `4.10`. ([#22395](https://github.com/expo/expo/pull/22395) by [@josephyanks](https://github.com/josephyanks))
|
|
18
|
+
|
|
19
|
+
### 🎉 New features
|
|
20
|
+
|
|
21
|
+
- On iOS, added native `ClipboardPasteButton` view that uses `UIPasteControl`. ([#22823](https://github.com/expo/expo/pull/22823) by [@alanjhughes](https://github.com/alanjhughes))
|
|
22
|
+
|
|
23
|
+
### 🐛 Bug fixes
|
|
24
|
+
|
|
25
|
+
- 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))
|
|
26
|
+
|
|
27
|
+
## 4.2.0 — 2023-05-08
|
|
28
|
+
|
|
29
|
+
_This version does not introduce any user-facing changes._
|
|
30
|
+
|
|
31
|
+
## 4.1.2 - 2023-03-03
|
|
14
32
|
|
|
15
33
|
### 🐛 Bug fixes
|
|
16
34
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
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
|
|
|
@@ -6,13 +13,11 @@
|
|
|
6
13
|
|
|
7
14
|
Please refer to the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/clipboard/).
|
|
8
15
|
|
|
9
|
-
## Installation
|
|
16
|
+
## Installation
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
`yarn add expo-clipboard && npx pod-install`
|
|
18
|
+
```
|
|
19
|
+
npx expo install expo-clipboard
|
|
20
|
+
```
|
|
16
21
|
|
|
17
22
|
## Contributing
|
|
18
23
|
|
package/android/build.gradle
CHANGED
|
@@ -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.
|
|
6
|
+
version = '4.3.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.
|
|
70
|
+
versionName '4.3.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 {
|
package/build/Clipboard.d.ts
CHANGED
|
@@ -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
|
package/build/Clipboard.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/build/Clipboard.js
CHANGED
|
@@ -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
|
package/build/Clipboard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["../src/Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,mBAAmB,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAS9F,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,aAAa,CAAC,CAAC;AAEhD,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAelD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAA4B,EAAE;IACjE,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE;QACjC,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;KAC9D;IACD,OAAO,MAAM,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,UAA4B,EAAE;IAE9B,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE;QACjC,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;KAC9D;IACD,OAAO,aAAa,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE;QACzB,2CAA2C;QAC3C,mCAAmC;QACnC,OAAO,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;KACtC;SAAM;QACL,cAAc,CAAC,IAAI,CAAC,CAAC;KACtB;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE;QACjC,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;KAC9D;IACD,OAAO,aAAa,CAAC,cAAc,EAAE,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE;QAC9B,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;KAC3D;IACD,OAAO,MAAM,aAAa,CAAC,WAAW,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW;IAC3C,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE;QAC9B,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;KAC3D;IACD,OAAO,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE;QAC9B,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;KAC3D;IACD,OAAO,MAAM,aAAa,CAAC,WAAW,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAwB;IAC1D,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE;QAChC,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;KAC7D;IACD,OAAO,MAAM,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,WAAmB;IACrD,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE;QAChC,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;KAC7D;IACD,OAAO,aAAa,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE;QAChC,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;KAC7D;IACD,OAAO,aAAa,CAAC,aAAa,EAAE,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAyC;IAC5E,gGAAgG;IAChG,MAAM,eAAe,GAAG,CAAC,KAAqB,EAAE,EAAE;QAChD,MAAM,YAAY,GAAmB;YACnC,GAAG,KAAK;YACR,IAAI,OAAO;gBACT,OAAO,CAAC,IAAI,CACV,sHAAsH,CACvH,CAAC;gBACF,OAAO,EAAE,CAAC;YACZ,CAAC;SACF,CAAC;QACF,QAAQ,CAAC,YAAY,CAAC,CAAC;IACzB,CAAC,CAAC;IACF,OAAO,OAAO,CAAC,WAAW,CAAiB,oBAAoB,EAAE,eAAe,CAAC,CAAC;AACpF,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,uBAAuB,CAAC,YAA0B;IAChE,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC;AAED,cAAc,mBAAmB,CAAC","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":"
|
|
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":"
|
|
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.pasteButtonIsAvailable()`](#pasteButtonIsAvailable)
|
|
6
|
+
* returns `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.pasteButtonIsAvailable()`](#pasteButtonIsAvailable)
|
|
8
|
+
* returns `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.pasteButtonIsAvailable()`](#pasteButtonIsAvailable)\n * returns `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 @@
|
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -7,7 +7,7 @@ import MobileCoreServices
|
|
|
7
7
|
|
|
8
8
|
class ClipboardModuleSpec: ExpoSpec {
|
|
9
9
|
override func spec() {
|
|
10
|
-
let appContext = AppContext()
|
|
10
|
+
let appContext = AppContext.create()
|
|
11
11
|
let holder = ModuleHolder(appContext: appContext, module: ClipboardModule(appContext: appContext))
|
|
12
12
|
|
|
13
13
|
func testModuleFunction<T>(_ functionName: String, args: [Any], _ block: @escaping (T?) -> Void) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-clipboard",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.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": "
|
|
42
|
+
"gitHead": "fa5ecca8251986b9f197cc14074eec0ab6dfb6db"
|
|
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 };
|
package/src/Clipboard.types.ts
CHANGED
|
@@ -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.pasteButtonIsAvailable()`](#pasteButtonIsAvailable)
|
|
12
|
+
* returns `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;
|