@symbiote-native/clipboard 3.0.2 → 3.0.3
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/README.md +62 -28
- package/build/angular/clipboard-paste-button/index.d.ts +15 -0
- package/build/angular/clipboard-paste-button/index.js +119 -0
- package/build/core/clipboard-paste-button.d.ts +29 -0
- package/build/core/clipboard-paste-button.js +50 -0
- package/build/core/clipboard.d.ts +2 -0
- package/build/core/clipboard.js +6 -1
- package/build/core/index.d.ts +3 -2
- package/build/core/index.js +2 -1
- package/build/core/native-module.d.ts +1 -0
- package/build/core/types.d.ts +11 -0
- package/build/core/types.js +1 -4
- package/build/react/clipboard-paste-button.d.ts +4 -0
- package/build/react/clipboard-paste-button.js +7 -0
- package/build/react/index.d.ts +1 -0
- package/build/react/index.js +1 -0
- package/build/solid/clipboard-paste-button.d.ts +3 -0
- package/build/solid/clipboard-paste-button.js +10 -0
- package/build/solid/index.d.ts +1 -0
- package/build/solid/index.js +1 -0
- package/build/svelte/clipboard-paste-button.svelte +17 -0
- package/build/svelte/clipboard-paste-button.svelte.d.ts +4 -0
- package/build/svelte/index.d.ts +1 -0
- package/build/svelte/index.js +1 -0
- package/build/vue/clipboard-paste-button.d.ts +3 -0
- package/build/vue/clipboard-paste-button.js +15 -0
- package/build/vue/index.d.ts +1 -0
- package/build/vue/index.js +1 -0
- package/build-ngc/angular/clipboard-paste-button/index.d.ts +18 -0
- package/build-ngc/angular/clipboard-paste-button/index.js +58 -0
- package/build-ngc/angular/clipboard-paste-button/index.js.map +1 -0
- package/build-ngc/core/clipboard-paste-button.d.ts +29 -0
- package/build-ngc/core/clipboard-paste-button.js +51 -0
- package/build-ngc/core/clipboard-paste-button.js.map +1 -0
- package/build-ngc/core/clipboard.d.ts +2 -0
- package/build-ngc/core/clipboard.js +6 -1
- package/build-ngc/core/clipboard.js.map +1 -1
- package/build-ngc/core/index.d.ts +3 -2
- package/build-ngc/core/index.js +2 -1
- package/build-ngc/core/index.js.map +1 -1
- package/build-ngc/core/native-module.d.ts +1 -0
- package/build-ngc/core/native-module.js.map +1 -1
- package/build-ngc/core/types.d.ts +11 -0
- package/build-ngc/core/types.js +1 -4
- package/build-ngc/core/types.js.map +1 -1
- package/package.json +17 -19
- package/src/angular/clipboard-paste-button/index.ts +40 -0
- package/src/core/clipboard-paste-button.ts +113 -0
- package/src/core/clipboard.ts +7 -1
- package/src/core/index.ts +14 -0
- package/src/core/native-module.ts +2 -0
- package/src/core/types.ts +19 -4
- package/src/react/clipboard-paste-button.ts +12 -0
- package/src/react/index.ts +1 -0
- package/src/solid/clipboard-paste-button.ts +15 -0
- package/src/solid/index.ts +1 -0
- package/src/svelte/clipboard-paste-button.svelte +17 -0
- package/src/svelte/index.ts +1 -0
- package/src/svelte/svelte-compile.test-helper.ts +6 -0
- package/src/vue/clipboard-paste-button.ts +16 -0
- package/src/vue/index.ts +1 -0
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# @symbiote-native/clipboard
|
|
2
2
|
|
|
3
3
|
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
-
[`expo-clipboard`](https://docs.expo.dev/versions/latest/sdk/clipboard/)
|
|
5
|
-
text, URLs, and images, plus a clipboard-change listener
|
|
4
|
+
[`expo-clipboard`](https://docs.expo.dev/versions/latest/sdk/clipboard/) - read/write clipboard
|
|
5
|
+
text, URLs, and images, plus a clipboard-change listener - usable from **every** adapter: React,
|
|
6
6
|
Vue, Svelte, Solid, and Angular, not just React. Unlike `@symbiote-native/sensors`, which is all
|
|
7
7
|
`DeviceSensor`-shaped classes plus one free-function module (`Pedometer`), clipboard is closer to
|
|
8
|
-
`@symbiote-native/local-auth`'s shape
|
|
8
|
+
`@symbiote-native/local-auth`'s shape - mostly stateless async functions - plus **one**
|
|
9
9
|
listener-based subscription (`addClipboardListener`) that each adapter wraps in its own
|
|
10
10
|
mount/unmount lifecycle (`useClipboard`).
|
|
11
11
|
|
|
@@ -23,38 +23,38 @@ npx @symbiote-native/cli new my-app --clipboard
|
|
|
23
23
|
npx @symbiote-native/cli add --clipboard
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Either way: installs `@symbiote-native/clipboard` and wires the native autolinking automatically
|
|
26
|
+
Either way: installs `@symbiote-native/clipboard` and wires the native autolinking automatically,
|
|
27
27
|
see [`@symbiote-native/cli`](../cli).
|
|
28
28
|
|
|
29
29
|
<details>
|
|
30
|
-
<summary>Manual install (no CLI
|
|
30
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
npm install @symbiote-native/clipboard
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Depends on `expo-clipboard` and `expo-modules-core` directly (regular dependencies, pinned to an
|
|
37
|
-
exact version
|
|
37
|
+
exact version - never a caret range, since this package's `core/` is hand-ported against one
|
|
38
38
|
specific native API shape and a newer resolve could silently drift the two apart). Never install
|
|
39
|
-
`expo-clipboard` yourself, and never add the `expo` meta-package to this project
|
|
39
|
+
`expo-clipboard` yourself, and never add the `expo` meta-package to this project - it bundles its
|
|
40
40
|
own Metro/Babel pipeline that conflicts with this project's own.
|
|
41
41
|
|
|
42
42
|
### Required one-time step: native autolinking wiring
|
|
43
43
|
|
|
44
44
|
Unlike a plain RN native module, `expo-clipboard`'s native code is discovered by
|
|
45
|
-
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism
|
|
45
|
+
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism - this needs wiring
|
|
46
46
|
into the native host app **once**, covering this package and every other `expo-modules-core`
|
|
47
47
|
package (`@symbiote-native/sensors`, `@symbiote-native/local-auth`) with zero further changes:
|
|
48
48
|
|
|
49
49
|
| Platform | Touches |
|
|
50
50
|
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
-
| iOS | `ios/Podfile`
|
|
52
|
-
| iOS | `AppDelegate.swift`
|
|
53
|
-
| Android | `settings.gradle` / `app/build.gradle`
|
|
54
|
-
| Android | `MainApplication.kt`
|
|
51
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
52
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
53
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
54
|
+
| Android | `MainApplication.kt` - Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
55
55
|
|
|
56
56
|
Full mechanics live in the `symbiote-expo-native-module` skill. Clipboard itself needs no
|
|
57
|
-
`Info.plist`/`AndroidManifest.xml` permission entry on either platform
|
|
57
|
+
`Info.plist`/`AndroidManifest.xml` permission entry on either platform - reading/writing the
|
|
58
58
|
clipboard requires no platform permission string.
|
|
59
59
|
|
|
60
60
|
</details>
|
|
@@ -67,19 +67,19 @@ src/core/ getStringAsync/setStringAsync/hasStringAsync, getUrlAsyn
|
|
|
67
67
|
addClipboardListener/removeClipboardListener. native-module.ts resolves
|
|
68
68
|
the ExpoClipboard native module via expo-modules-core's
|
|
69
69
|
requireNativeModule.
|
|
70
|
-
src/react/hooks/ @symbiote-native/clipboard/react
|
|
71
|
-
src/vue/composables/ @symbiote-native/clipboard/vue
|
|
72
|
-
src/svelte/runes/ @symbiote-native/clipboard/svelte
|
|
73
|
-
src/solid/primitives/ @symbiote-native/clipboard/solid
|
|
70
|
+
src/react/hooks/ @symbiote-native/clipboard/react - useClipboard
|
|
71
|
+
src/vue/composables/ @symbiote-native/clipboard/vue - useClipboard (same name)
|
|
72
|
+
src/svelte/runes/ @symbiote-native/clipboard/svelte - useClipboard (same name)
|
|
73
|
+
src/solid/primitives/ @symbiote-native/clipboard/solid - createClipboard (Solid says
|
|
74
74
|
create*, not use*)
|
|
75
|
-
src/angular/services/ @symbiote-native/clipboard/angular
|
|
75
|
+
src/angular/services/ @symbiote-native/clipboard/angular - ClipboardService
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
Every function except the listener is plain stateless async and re-exported as-is. The listener
|
|
79
79
|
subscription (`addClipboardListener`) lives once in `core`, framework-agnostic; each adapter's
|
|
80
80
|
`useClipboard` hook/composable/rune, Solid's `createClipboard`, and `ClipboardService.connect()`
|
|
81
81
|
are thin mount/unmount wrappers (a DI-scoped `effect()` for Angular, a body-level subscription plus
|
|
82
|
-
`onCleanup` for Solid) around that same subscription
|
|
82
|
+
`onCleanup` for Solid) around that same subscription - the plumbing is written once and shared by
|
|
83
83
|
every adapter.
|
|
84
84
|
|
|
85
85
|
## Use it
|
|
@@ -93,7 +93,7 @@ import {
|
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
`useClipboard()`'s event only carries the clipboard's changed content _types_
|
|
96
|
-
(`IClipboardEvent.contentTypes`), never the string itself
|
|
96
|
+
(`IClipboardEvent.contentTypes`), never the string itself - every adapter's demo screen treats a
|
|
97
97
|
change as a cue to re-fetch via `getStringAsync()`, not a value to render directly.
|
|
98
98
|
|
|
99
99
|
```tsx
|
|
@@ -234,10 +234,10 @@ export class ClipboardScreen {
|
|
|
234
234
|
}
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
-
These are trimmed from the real demo screens
|
|
237
|
+
These are trimmed from the real demo screens - `examples/expo-react/screens/ClipboardScreen.tsx`,
|
|
238
238
|
`examples/expo-vue-sfc/screens/ClipboardScreen.vue`, `examples/expo-vue-tsx/screens/ClipboardScreen.tsx`,
|
|
239
239
|
`examples/expo-svelte/screens/ClipboardScreen.svelte`, `examples/expo-solid/screens/ClipboardScreen.tsx`,
|
|
240
|
-
`examples/expo-angular/src/screens/ClipboardScreen.ts`
|
|
240
|
+
`examples/expo-angular/src/screens/ClipboardScreen.ts` - which additionally show `hasStringAsync()`
|
|
241
241
|
status badges and, iOS-only, the `getUrlAsync`/`setUrlAsync`/`hasUrlAsync` URL surface.
|
|
242
242
|
|
|
243
243
|
## API
|
|
@@ -257,7 +257,8 @@ removeClipboardListener(subscription: EventSubscription) // deprecated, use subs
|
|
|
257
257
|
```
|
|
258
258
|
|
|
259
259
|
Plus `ContentType`, `StringFormat`, `IGetStringOptions`, `ISetStringOptions`, `IGetImageOptions`,
|
|
260
|
-
`IClipboardImage`, `IClipboardEvent`
|
|
260
|
+
`IClipboardImage`, `IClipboardEvent`, `IAcceptedContentType`, `ICornerStyle`, `IDisplayMode` and
|
|
261
|
+
`IPasteEventPayload`, ported from upstream's `Clipboard.types.ts`, renamed with
|
|
261
262
|
this repo's `I`-prefix convention for exported types (`ts-js-best-practices`); `ContentType` and
|
|
262
263
|
`StringFormat` stay unprefixed enums, matching `AuthenticationType`/`SecurityLevel` in
|
|
263
264
|
`@symbiote-native/local-auth`.
|
|
@@ -284,15 +285,48 @@ import { ClipboardService } from '@symbiote-native/clipboard/angular';
|
|
|
284
285
|
readonly clipboardEvent = inject(ClipboardService).connect(); // Signal<IClipboardEvent | null>
|
|
285
286
|
```
|
|
286
287
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
`
|
|
288
|
+
### ClipboardPasteButton
|
|
289
|
+
|
|
290
|
+
`UIPasteControl`, iOS 16+, lets the user paste without the system permission prompt. It renders
|
|
291
|
+
nothing on Android, and `isPasteButtonAvailable` (from the package root) tells whether the device
|
|
292
|
+
supports it. Give it a width and a height in `style`, or it does not appear. Apple restricts the
|
|
293
|
+
control, so `style` takes no `backgroundColor`, `borderRadius` or `color`: use `backgroundColor`,
|
|
294
|
+
`foregroundColor`, `cornerStyle` and `displayMode` instead.
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
// React, `onPress` gets `{ type: 'text', text }` or `{ type: 'image', data, size }`
|
|
298
|
+
import { ClipboardPasteButton, isPasteButtonAvailable } from '@symbiote-native/clipboard/react';
|
|
299
|
+
|
|
300
|
+
{isPasteButtonAvailable && (
|
|
301
|
+
<ClipboardPasteButton
|
|
302
|
+
style={{ width: 160, height: 44 }}
|
|
303
|
+
cornerStyle="capsule"
|
|
304
|
+
acceptedContentTypes={['plain-text', 'image']}
|
|
305
|
+
onPress={data => (data.type === 'text' ? setText(data.text) : setImage(data.data))}
|
|
306
|
+
/>
|
|
307
|
+
)}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Vue takes `@press`, Angular `[onPress]`, Svelte and Solid `onPress`, all from their own entry
|
|
311
|
+
(`ClipboardPasteButton`). It is an Expo native view reached through `requireNativeViewManager`,
|
|
312
|
+
which the core registers when the button renders. The pasted payload is read through a guard in
|
|
313
|
+
`toPasteEventPayload`, a malformed native event is dropped.
|
|
314
|
+
|
|
315
|
+
## Common questions
|
|
316
|
+
|
|
317
|
+
- **Empty string on iOS.** A denied paste on iOS 16+ returns `''`, indistinguishable from an empty
|
|
318
|
+
clipboard; no error is thrown.
|
|
319
|
+
- **Avoid the paste prompt?** Use `ClipboardPasteButton` (wraps `UIPasteControl`).
|
|
320
|
+
- **Copy an image?** `setImageAsync` takes a base64 string.
|
|
321
|
+
|
|
322
|
+
Sources: [Expo docs: Clipboard](https://docs.expo.dev/versions/latest/sdk/clipboard/),
|
|
323
|
+
[expo/expo#40189](https://github.com/expo/expo/issues/40189).
|
|
290
324
|
|
|
291
325
|
## Test it
|
|
292
326
|
|
|
293
327
|
Tests exercise the JS layer only, against a fake native module in place of the real
|
|
294
328
|
`requireNativeModule` resolution (`src/core/**/*.test.ts`,
|
|
295
|
-
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`)
|
|
329
|
+
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`) - no Fabric/Descriptor angle at all,
|
|
296
330
|
since clipboard
|
|
297
331
|
is a pure async-function + one-listener surface, never a view. Native rendering itself is verified
|
|
298
332
|
on-device (see the parent [README](../../README.md) for the project's testing model).
|
|
@@ -300,4 +334,4 @@ on-device (see the parent [README](../../README.md) for the project's testing mo
|
|
|
300
334
|
Native autolinking wiring (Android's 3-layer registration, iOS Podfile/pod install) is done across
|
|
301
335
|
all six `examples/expo-*` canary apps. It isn't wired into the public non-Expo canaries
|
|
302
336
|
(`examples/react`, `examples/vue-sfc`, `examples/vue-tsx`, `examples/svelte`, `examples/solid`,
|
|
303
|
-
`examples/angular`) yet
|
|
337
|
+
`examples/angular`) yet - those don't depend on any `expo-modules-core` package today.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NativeViewBase } from '@symbiote-native/angular';
|
|
2
|
+
import type { IDescriptor } from '@symbiote-native/components';
|
|
3
|
+
import type { IClipboardPasteButtonProps } from '../../core/clipboard-paste-button';
|
|
4
|
+
/** Angular twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
|
|
5
|
+
export declare class ClipboardPasteButton extends NativeViewBase {
|
|
6
|
+
onPress: IClipboardPasteButtonProps['onPress'];
|
|
7
|
+
backgroundColor?: IClipboardPasteButtonProps['backgroundColor'];
|
|
8
|
+
foregroundColor?: IClipboardPasteButtonProps['foregroundColor'];
|
|
9
|
+
cornerStyle?: IClipboardPasteButtonProps['cornerStyle'];
|
|
10
|
+
displayMode?: IClipboardPasteButtonProps['displayMode'];
|
|
11
|
+
imageOptions?: IClipboardPasteButtonProps['imageOptions'];
|
|
12
|
+
acceptedContentTypes?: IClipboardPasteButtonProps['acceptedContentTypes'];
|
|
13
|
+
protected readonly propNames: readonly ["onPress", "backgroundColor", "foregroundColor", "cornerStyle", "displayMode", "imageOptions", "acceptedContentTypes"];
|
|
14
|
+
protected renderView(props: object): IDescriptor | null;
|
|
15
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
|
36
|
+
import { DescriptorOutlet, NativeViewBase } from '@symbiote-native/angular';
|
|
37
|
+
import { renderClipboardPasteButton } from '../../core/clipboard-paste-button.js';
|
|
38
|
+
/** Angular twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
|
|
39
|
+
let ClipboardPasteButton = (() => {
|
|
40
|
+
let _classDecorators = [Component({
|
|
41
|
+
selector: 'ClipboardPasteButton',
|
|
42
|
+
standalone: true,
|
|
43
|
+
imports: [DescriptorOutlet],
|
|
44
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
45
|
+
template: `@if (descriptor; as node) {
|
|
46
|
+
<symbiote-descriptor-outlet [node]="node" />
|
|
47
|
+
}`,
|
|
48
|
+
})];
|
|
49
|
+
let _classDescriptor;
|
|
50
|
+
let _classExtraInitializers = [];
|
|
51
|
+
let _classThis;
|
|
52
|
+
let _classSuper = NativeViewBase;
|
|
53
|
+
let _onPress_decorators;
|
|
54
|
+
let _onPress_initializers = [];
|
|
55
|
+
let _onPress_extraInitializers = [];
|
|
56
|
+
let _backgroundColor_decorators;
|
|
57
|
+
let _backgroundColor_initializers = [];
|
|
58
|
+
let _backgroundColor_extraInitializers = [];
|
|
59
|
+
let _foregroundColor_decorators;
|
|
60
|
+
let _foregroundColor_initializers = [];
|
|
61
|
+
let _foregroundColor_extraInitializers = [];
|
|
62
|
+
let _cornerStyle_decorators;
|
|
63
|
+
let _cornerStyle_initializers = [];
|
|
64
|
+
let _cornerStyle_extraInitializers = [];
|
|
65
|
+
let _displayMode_decorators;
|
|
66
|
+
let _displayMode_initializers = [];
|
|
67
|
+
let _displayMode_extraInitializers = [];
|
|
68
|
+
let _imageOptions_decorators;
|
|
69
|
+
let _imageOptions_initializers = [];
|
|
70
|
+
let _imageOptions_extraInitializers = [];
|
|
71
|
+
let _acceptedContentTypes_decorators;
|
|
72
|
+
let _acceptedContentTypes_initializers = [];
|
|
73
|
+
let _acceptedContentTypes_extraInitializers = [];
|
|
74
|
+
var ClipboardPasteButton = class extends _classSuper {
|
|
75
|
+
static { _classThis = this; }
|
|
76
|
+
static {
|
|
77
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
78
|
+
_onPress_decorators = [Input({ required: true })];
|
|
79
|
+
_backgroundColor_decorators = [Input()];
|
|
80
|
+
_foregroundColor_decorators = [Input()];
|
|
81
|
+
_cornerStyle_decorators = [Input()];
|
|
82
|
+
_displayMode_decorators = [Input()];
|
|
83
|
+
_imageOptions_decorators = [Input()];
|
|
84
|
+
_acceptedContentTypes_decorators = [Input()];
|
|
85
|
+
__esDecorate(null, null, _onPress_decorators, { kind: "field", name: "onPress", static: false, private: false, access: { has: obj => "onPress" in obj, get: obj => obj.onPress, set: (obj, value) => { obj.onPress = value; } }, metadata: _metadata }, _onPress_initializers, _onPress_extraInitializers);
|
|
86
|
+
__esDecorate(null, null, _backgroundColor_decorators, { kind: "field", name: "backgroundColor", static: false, private: false, access: { has: obj => "backgroundColor" in obj, get: obj => obj.backgroundColor, set: (obj, value) => { obj.backgroundColor = value; } }, metadata: _metadata }, _backgroundColor_initializers, _backgroundColor_extraInitializers);
|
|
87
|
+
__esDecorate(null, null, _foregroundColor_decorators, { kind: "field", name: "foregroundColor", static: false, private: false, access: { has: obj => "foregroundColor" in obj, get: obj => obj.foregroundColor, set: (obj, value) => { obj.foregroundColor = value; } }, metadata: _metadata }, _foregroundColor_initializers, _foregroundColor_extraInitializers);
|
|
88
|
+
__esDecorate(null, null, _cornerStyle_decorators, { kind: "field", name: "cornerStyle", static: false, private: false, access: { has: obj => "cornerStyle" in obj, get: obj => obj.cornerStyle, set: (obj, value) => { obj.cornerStyle = value; } }, metadata: _metadata }, _cornerStyle_initializers, _cornerStyle_extraInitializers);
|
|
89
|
+
__esDecorate(null, null, _displayMode_decorators, { kind: "field", name: "displayMode", static: false, private: false, access: { has: obj => "displayMode" in obj, get: obj => obj.displayMode, set: (obj, value) => { obj.displayMode = value; } }, metadata: _metadata }, _displayMode_initializers, _displayMode_extraInitializers);
|
|
90
|
+
__esDecorate(null, null, _imageOptions_decorators, { kind: "field", name: "imageOptions", static: false, private: false, access: { has: obj => "imageOptions" in obj, get: obj => obj.imageOptions, set: (obj, value) => { obj.imageOptions = value; } }, metadata: _metadata }, _imageOptions_initializers, _imageOptions_extraInitializers);
|
|
91
|
+
__esDecorate(null, null, _acceptedContentTypes_decorators, { kind: "field", name: "acceptedContentTypes", static: false, private: false, access: { has: obj => "acceptedContentTypes" in obj, get: obj => obj.acceptedContentTypes, set: (obj, value) => { obj.acceptedContentTypes = value; } }, metadata: _metadata }, _acceptedContentTypes_initializers, _acceptedContentTypes_extraInitializers);
|
|
92
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
93
|
+
ClipboardPasteButton = _classThis = _classDescriptor.value;
|
|
94
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
95
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
96
|
+
}
|
|
97
|
+
onPress = __runInitializers(this, _onPress_initializers, void 0);
|
|
98
|
+
backgroundColor = (__runInitializers(this, _onPress_extraInitializers), __runInitializers(this, _backgroundColor_initializers, void 0));
|
|
99
|
+
foregroundColor = (__runInitializers(this, _backgroundColor_extraInitializers), __runInitializers(this, _foregroundColor_initializers, void 0));
|
|
100
|
+
cornerStyle = (__runInitializers(this, _foregroundColor_extraInitializers), __runInitializers(this, _cornerStyle_initializers, void 0));
|
|
101
|
+
displayMode = (__runInitializers(this, _cornerStyle_extraInitializers), __runInitializers(this, _displayMode_initializers, void 0));
|
|
102
|
+
imageOptions = (__runInitializers(this, _displayMode_extraInitializers), __runInitializers(this, _imageOptions_initializers, void 0));
|
|
103
|
+
acceptedContentTypes = (__runInitializers(this, _imageOptions_extraInitializers), __runInitializers(this, _acceptedContentTypes_initializers, void 0));
|
|
104
|
+
propNames = (__runInitializers(this, _acceptedContentTypes_extraInitializers), [
|
|
105
|
+
'onPress',
|
|
106
|
+
'backgroundColor',
|
|
107
|
+
'foregroundColor',
|
|
108
|
+
'cornerStyle',
|
|
109
|
+
'displayMode',
|
|
110
|
+
'imageOptions',
|
|
111
|
+
'acceptedContentTypes',
|
|
112
|
+
]);
|
|
113
|
+
renderView(props) {
|
|
114
|
+
return renderClipboardPasteButton(props);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
return ClipboardPasteButton = _classThis;
|
|
118
|
+
})();
|
|
119
|
+
export { ClipboardPasteButton };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IAccessibilityProps, IAriaProps, IDescriptor, IResponderProps } from '@symbiote-native/components';
|
|
2
|
+
import type { IStyleProp, ISymbioteEvent, IViewStyle } from '@symbiote-native/engine';
|
|
3
|
+
import type { IAcceptedContentType, ICornerStyle, IDisplayMode, IGetImageOptions, IPasteEventPayload } from './types';
|
|
4
|
+
export declare const CLIPBOARD_PASTE_BUTTON_MODULE_NAME = "ExpoClipboard";
|
|
5
|
+
export type IClipboardPasteButtonProps = IAccessibilityProps & IAriaProps & IResponderProps & {
|
|
6
|
+
/** Called with the pasted text or image, inspect `type` to tell them apart */
|
|
7
|
+
onPress: (data: IPasteEventPayload) => void;
|
|
8
|
+
/** Leaving it out lets the color follow the system theme */
|
|
9
|
+
backgroundColor?: string | null;
|
|
10
|
+
/** @default 'white' */
|
|
11
|
+
foregroundColor?: string | null;
|
|
12
|
+
/** @default 'capsule' */
|
|
13
|
+
cornerStyle?: ICornerStyle | null;
|
|
14
|
+
/** @default 'iconAndLabel' */
|
|
15
|
+
displayMode?: IDisplayMode | null;
|
|
16
|
+
/** Needs a width and a height, or the button does not appear */
|
|
17
|
+
style?: IStyleProp<Omit<IViewStyle, 'backgroundColor' | 'borderRadius' | 'color'>>;
|
|
18
|
+
imageOptions?: IGetImageOptions | null;
|
|
19
|
+
/** Do not combine `plain-text` with `html`, all text would be treated as `html` */
|
|
20
|
+
acceptedContentTypes?: IAcceptedContentType[];
|
|
21
|
+
testID?: string;
|
|
22
|
+
nativeID?: string;
|
|
23
|
+
onLayout?: (event: ISymbioteEvent) => void;
|
|
24
|
+
};
|
|
25
|
+
export declare function clipboardPasteButtonViewName(): string;
|
|
26
|
+
export declare function ensureClipboardPasteButtonRegistered(): boolean;
|
|
27
|
+
/** Reads the native paste event, `null` when its payload has neither a text nor an image shape */
|
|
28
|
+
export declare function toPasteEventPayload(event: ISymbioteEvent): IPasteEventPayload | null;
|
|
29
|
+
export declare function renderClipboardPasteButton(props: object): IDescriptor | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Platform, requireNativeViewManager } from 'expo-modules-core';
|
|
2
|
+
import { el } from '@symbiote-native/components';
|
|
3
|
+
import { expoViewManagerName, tryRegisterNativeView, } from '@symbiote-native/engine';
|
|
4
|
+
export const CLIPBOARD_PASTE_BUTTON_MODULE_NAME = 'ExpoClipboard';
|
|
5
|
+
const PASTE_EVENT_PROP = 'onPastePressed';
|
|
6
|
+
const PASTE_TYPE = { text: 'text', image: 'image' };
|
|
7
|
+
export function clipboardPasteButtonViewName() {
|
|
8
|
+
return expoViewManagerName(CLIPBOARD_PASTE_BUTTON_MODULE_NAME);
|
|
9
|
+
}
|
|
10
|
+
// Registers the view config in React Native's registry, which the engine derives the view's events
|
|
11
|
+
// and prop processors from. Called at render, not at import: a registration that only a barrel's
|
|
12
|
+
// load-time side effect performs is dropped by Metro's `inlineRequires` in release
|
|
13
|
+
export function ensureClipboardPasteButtonRegistered() {
|
|
14
|
+
if (!Platform.select({ ios: true, default: false }))
|
|
15
|
+
return false;
|
|
16
|
+
return tryRegisterNativeView(() => requireNativeViewManager(CLIPBOARD_PASTE_BUTTON_MODULE_NAME));
|
|
17
|
+
}
|
|
18
|
+
function isImageSize(value) {
|
|
19
|
+
return (typeof value === 'object' &&
|
|
20
|
+
value !== null &&
|
|
21
|
+
typeof Reflect.get(value, 'width') === 'number' &&
|
|
22
|
+
typeof Reflect.get(value, 'height') === 'number');
|
|
23
|
+
}
|
|
24
|
+
/** Reads the native paste event, `null` when its payload has neither a text nor an image shape */
|
|
25
|
+
export function toPasteEventPayload(event) {
|
|
26
|
+
const { type, text, data, size } = event.nativeEvent;
|
|
27
|
+
if (type === PASTE_TYPE.text && typeof text === 'string')
|
|
28
|
+
return { type, text };
|
|
29
|
+
if (type === PASTE_TYPE.image &&
|
|
30
|
+
typeof data === 'string' &&
|
|
31
|
+
isImageSize(size)) {
|
|
32
|
+
return { type, data, size };
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
// `object`, not the props type: Vue and Angular hand over an untyped attribute bag
|
|
37
|
+
export function renderClipboardPasteButton(props) {
|
|
38
|
+
if (!ensureClipboardPasteButtonRegistered())
|
|
39
|
+
return null;
|
|
40
|
+
const onPress = Reflect.get(props, 'onPress');
|
|
41
|
+
const viewProps = Object.fromEntries(Object.entries(props).filter(([key]) => key !== 'onPress'));
|
|
42
|
+
return el(clipboardPasteButtonViewName(), {
|
|
43
|
+
...viewProps,
|
|
44
|
+
[PASTE_EVENT_PROP]: (event) => {
|
|
45
|
+
const payload = toPasteEventPayload(event);
|
|
46
|
+
if (payload && typeof onPress === 'function')
|
|
47
|
+
onPress(payload);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -79,6 +79,8 @@ export declare function hasImageAsync(): Promise<boolean>;
|
|
|
79
79
|
* content types whenever the clipboard changes.
|
|
80
80
|
*/
|
|
81
81
|
export declare function addClipboardListener(listener: (event: IClipboardEvent) => void): EventSubscription;
|
|
82
|
+
/** Whether `ClipboardPasteButton` can render, needs iOS 16 or newer */
|
|
83
|
+
export declare const isPasteButtonAvailable: boolean;
|
|
82
84
|
/**
|
|
83
85
|
* Removes the listener added by `addClipboardListener`.
|
|
84
86
|
* @deprecated use `subscription.remove()` instead.
|
package/build/core/clipboard.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UnavailabilityError } from 'expo-modules-core';
|
|
1
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
2
2
|
import { CLIPBOARD_CHANGED_EVENT_NAME, expoClipboard } from './native-module.js';
|
|
3
3
|
// Matches the module name upstream's own Clipboard.ts passes to every UnavailabilityError.
|
|
4
4
|
const NATIVE_MODULE_NAME = 'Clipboard';
|
|
@@ -133,6 +133,11 @@ export async function hasImageAsync() {
|
|
|
133
133
|
export function addClipboardListener(listener) {
|
|
134
134
|
return expoClipboard.addListener(CLIPBOARD_CHANGED_EVENT_NAME, listener);
|
|
135
135
|
}
|
|
136
|
+
/** Whether `ClipboardPasteButton` can render, needs iOS 16 or newer */
|
|
137
|
+
export const isPasteButtonAvailable = Platform.select({
|
|
138
|
+
ios: expoClipboard.isPasteButtonAvailable ?? false,
|
|
139
|
+
default: false,
|
|
140
|
+
});
|
|
136
141
|
/**
|
|
137
142
|
* Removes the listener added by `addClipboardListener`.
|
|
138
143
|
* @deprecated use `subscription.remove()` instead.
|
package/build/core/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { getStringAsync, setStringAsync, hasStringAsync, getUrlAsync, setUrlAsync, hasUrlAsync, getImageAsync, setImageAsync, hasImageAsync, addClipboardListener, removeClipboardListener, } from './clipboard';
|
|
2
|
-
export {
|
|
1
|
+
export { getStringAsync, setStringAsync, hasStringAsync, getUrlAsync, setUrlAsync, hasUrlAsync, getImageAsync, setImageAsync, hasImageAsync, addClipboardListener, removeClipboardListener, isPasteButtonAvailable, } from './clipboard';
|
|
2
|
+
export { clipboardPasteButtonViewName, ensureClipboardPasteButtonRegistered, renderClipboardPasteButton, toPasteEventPayload, type IClipboardPasteButtonProps, } from './clipboard-paste-button';
|
|
3
|
+
export { ContentType, StringFormat, type IGetStringOptions, type ISetStringOptions, type IGetImageOptions, type IClipboardImage, type IClipboardEvent, type IAcceptedContentType, type ICornerStyle, type IDisplayMode, type ITextPasteEvent, type IImagePasteEvent, type IPasteEventPayload, } from './types';
|
|
3
4
|
export type { EventSubscription } from 'expo-modules-core';
|
package/build/core/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { getStringAsync, setStringAsync, hasStringAsync, getUrlAsync, setUrlAsync, hasUrlAsync, getImageAsync, setImageAsync, hasImageAsync, addClipboardListener, removeClipboardListener, } from './clipboard.js';
|
|
1
|
+
export { getStringAsync, setStringAsync, hasStringAsync, getUrlAsync, setUrlAsync, hasUrlAsync, getImageAsync, setImageAsync, hasImageAsync, addClipboardListener, removeClipboardListener, isPasteButtonAvailable, } from './clipboard.js';
|
|
2
|
+
export { clipboardPasteButtonViewName, ensureClipboardPasteButtonRegistered, renderClipboardPasteButton, toPasteEventPayload, } from './clipboard-paste-button.js';
|
|
2
3
|
export { ContentType, StringFormat, } from './types.js';
|
|
@@ -13,5 +13,6 @@ export type INativeClipboardModule = {
|
|
|
13
13
|
getImageAsync?(options: IGetImageOptions): Promise<IClipboardImage | null>;
|
|
14
14
|
setImageAsync?(base64Image: string): Promise<void>;
|
|
15
15
|
hasImageAsync?(): Promise<boolean>;
|
|
16
|
+
isPasteButtonAvailable?: boolean;
|
|
16
17
|
};
|
|
17
18
|
export declare const expoClipboard: INativeClipboardModule;
|
package/build/core/types.d.ts
CHANGED
|
@@ -65,3 +65,14 @@ export type IClipboardEvent = {
|
|
|
65
65
|
*/
|
|
66
66
|
contentTypes: ContentType[];
|
|
67
67
|
};
|
|
68
|
+
export type IAcceptedContentType = 'plain-text' | 'image' | 'url' | 'html';
|
|
69
|
+
export type ICornerStyle = 'dynamic' | 'fixed' | 'capsule' | 'large' | 'medium' | 'small';
|
|
70
|
+
export type IDisplayMode = 'iconAndLabel' | 'iconOnly' | 'labelOnly';
|
|
71
|
+
export type ITextPasteEvent = {
|
|
72
|
+
text: string;
|
|
73
|
+
type: 'text';
|
|
74
|
+
};
|
|
75
|
+
export type IImagePasteEvent = {
|
|
76
|
+
type: 'image';
|
|
77
|
+
} & IClipboardImage;
|
|
78
|
+
export type IPasteEventPayload = ITextPasteEvent | IImagePasteEvent;
|
package/build/core/types.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
// Ported from expo-clipboard's own Clipboard.types.ts. ContentType and StringFormat are enums —
|
|
2
2
|
// kept unprefixed, matching this repo's convention for enums (see AuthenticationType /
|
|
3
3
|
// SecurityLevel in @symbiote-native/local-auth); the plain option/result shapes get this repo's
|
|
4
|
-
// `I`-prefix convention for exported types
|
|
5
|
-
// (PasteEventPayload, TextPasteEvent, ImagePasteEvent, AcceptedContentType, CornerStyleType,
|
|
6
|
-
// DisplayModeType) are intentionally not ported — that native paste-button view is out of scope
|
|
7
|
-
// for this pass (see the package README).
|
|
4
|
+
// `I`-prefix convention for exported types
|
|
8
5
|
/**
|
|
9
6
|
* Type used to define what type of data is stored in the clipboard.
|
|
10
7
|
*/
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { IClipboardPasteButtonProps } from '../core/clipboard-paste-button';
|
|
3
|
+
/** React twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
|
|
4
|
+
export declare function ClipboardPasteButton(props: IClipboardPasteButtonProps): ReactElement | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { descriptorToReact } from '@symbiote-native/react';
|
|
2
|
+
import { renderClipboardPasteButton } from '../core/clipboard-paste-button.js';
|
|
3
|
+
/** React twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
|
|
4
|
+
export function ClipboardPasteButton(props) {
|
|
5
|
+
const descriptor = renderClipboardPasteButton(props);
|
|
6
|
+
return descriptor ? descriptorToReact(descriptor) : null;
|
|
7
|
+
}
|
package/build/react/index.d.ts
CHANGED
package/build/react/index.js
CHANGED
|
@@ -5,4 +5,5 @@
|
|
|
5
5
|
// adapters/react/src/hooks (never `composables`, that's Vue's term) and the shape of
|
|
6
6
|
// @symbiote-native/sensors' useAccelerometer.
|
|
7
7
|
export { useClipboard } from './hooks/use-clipboard/index.js';
|
|
8
|
+
export { ClipboardPasteButton } from './clipboard-paste-button.js';
|
|
8
9
|
export * from '../core/index.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { descriptorToSolid } from '@symbiote-native/solid';
|
|
2
|
+
import { renderClipboardPasteButton } from '../core/clipboard-paste-button.js';
|
|
3
|
+
// The platform decision is made once at mount: the descriptor bridge builds the node once and
|
|
4
|
+
// only diffs prop values afterwards, so the descriptor's shape must not change
|
|
5
|
+
export function ClipboardPasteButton(props) {
|
|
6
|
+
const initial = renderClipboardPasteButton({ ...props });
|
|
7
|
+
if (!initial)
|
|
8
|
+
return null;
|
|
9
|
+
return descriptorToSolid(() => renderClipboardPasteButton({ ...props }) ?? initial);
|
|
10
|
+
}
|
package/build/solid/index.d.ts
CHANGED
package/build/solid/index.js
CHANGED
|
@@ -5,4 +5,5 @@
|
|
|
5
5
|
// `create*` (never `hooks/`/`use*`, that is React's term and Solid's word for consuming an
|
|
6
6
|
// existing thing) — see adapters/solid/src/primitives for the same convention.
|
|
7
7
|
export { createClipboard } from './primitives/create-clipboard.js';
|
|
8
|
+
export { ClipboardPasteButton } from './clipboard-paste-button.js';
|
|
8
9
|
export * from '../core/index.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// The native view name is mixed-case, which a literal tag would parse as a component reference,
|
|
3
|
+
// so the tag is dynamic
|
|
4
|
+
import { hostProps } from '@symbiote-native/svelte/native-view-bridge';
|
|
5
|
+
import { renderClipboardPasteButton } from '../core/clipboard-paste-button';
|
|
6
|
+
import type { IClipboardPasteButtonProps } from '../core/clipboard-paste-button';
|
|
7
|
+
|
|
8
|
+
const props: IClipboardPasteButtonProps = $props();
|
|
9
|
+
const descriptor = $derived(renderClipboardPasteButton({ ...props }));
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
{#if descriptor}
|
|
13
|
+
<svelte:element
|
|
14
|
+
this={descriptor.type}
|
|
15
|
+
{@attach hostProps(descriptor.props)}
|
|
16
|
+
/>
|
|
17
|
+
{/if}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IClipboardPasteButtonProps } from '../core/clipboard-paste-button';
|
|
2
|
+
declare const ClipboardPasteButton: import("svelte").Component<IClipboardPasteButtonProps, {}, "">;
|
|
3
|
+
type ClipboardPasteButton = ReturnType<typeof ClipboardPasteButton>;
|
|
4
|
+
export default ClipboardPasteButton;
|
package/build/svelte/index.d.ts
CHANGED
package/build/svelte/index.js
CHANGED
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
// `$effect` (runes/use-clipboard.svelte.ts), mirroring the lifecycle-bucket naming convention of
|
|
5
5
|
// adapters/svelte/src/runes (never `hooks/`/`composables/`, those are React's and Vue's terms).
|
|
6
6
|
export { useClipboard } from './runes/use-clipboard.svelte';
|
|
7
|
+
export { default as ClipboardPasteButton } from './clipboard-paste-button.svelte';
|
|
7
8
|
export * from '../core/index.js';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type VNode } from '@vue/runtime-core';
|
|
2
|
+
/** Vue twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
|
|
3
|
+
export declare const ClipboardPasteButton: import("vue").DefineComponent<{}, () => VNode | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineComponent } from '@vue/runtime-core';
|
|
2
|
+
import { descriptorToVue, normalizeVueAttrs } from '@symbiote-native/vue';
|
|
3
|
+
import { renderClipboardPasteButton } from '../core/clipboard-paste-button.js';
|
|
4
|
+
/** Vue twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
|
|
5
|
+
export const ClipboardPasteButton = defineComponent({
|
|
6
|
+
name: 'ClipboardPasteButton',
|
|
7
|
+
// Every attribute is a native view prop, so none falls through onto a wrapper element
|
|
8
|
+
inheritAttrs: false,
|
|
9
|
+
setup(_props, { attrs }) {
|
|
10
|
+
return () => {
|
|
11
|
+
const descriptor = renderClipboardPasteButton(normalizeVueAttrs(attrs));
|
|
12
|
+
return descriptor ? descriptorToVue(descriptor) : null;
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
});
|
package/build/vue/index.d.ts
CHANGED
package/build/vue/index.js
CHANGED
|
@@ -5,4 +5,5 @@
|
|
|
5
5
|
// convention of adapters/vue/src/composables (never `hooks/`, that's React's term) and the
|
|
6
6
|
// shape of @symbiote-native/sensors' useAccelerometer composable.
|
|
7
7
|
export { useClipboard } from './composables/use-clipboard/index.js';
|
|
8
|
+
export { ClipboardPasteButton } from './clipboard-paste-button.js';
|
|
8
9
|
export * from '../core/index.js';
|