@symbiote-native/clipboard 3.0.1 → 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.
Files changed (61) hide show
  1. package/README.md +62 -28
  2. package/build/angular/clipboard-paste-button/index.d.ts +15 -0
  3. package/build/angular/clipboard-paste-button/index.js +119 -0
  4. package/build/core/clipboard-paste-button.d.ts +29 -0
  5. package/build/core/clipboard-paste-button.js +50 -0
  6. package/build/core/clipboard.d.ts +2 -0
  7. package/build/core/clipboard.js +6 -1
  8. package/build/core/index.d.ts +3 -2
  9. package/build/core/index.js +2 -1
  10. package/build/core/native-module.d.ts +1 -0
  11. package/build/core/types.d.ts +11 -0
  12. package/build/core/types.js +1 -4
  13. package/build/react/clipboard-paste-button.d.ts +4 -0
  14. package/build/react/clipboard-paste-button.js +7 -0
  15. package/build/react/index.d.ts +1 -0
  16. package/build/react/index.js +1 -0
  17. package/build/solid/clipboard-paste-button.d.ts +3 -0
  18. package/build/solid/clipboard-paste-button.js +10 -0
  19. package/build/solid/index.d.ts +1 -0
  20. package/build/solid/index.js +1 -0
  21. package/build/svelte/clipboard-paste-button.svelte +17 -0
  22. package/build/svelte/clipboard-paste-button.svelte.d.ts +4 -0
  23. package/build/svelte/index.d.ts +1 -0
  24. package/build/svelte/index.js +1 -0
  25. package/build/vue/clipboard-paste-button.d.ts +3 -0
  26. package/build/vue/clipboard-paste-button.js +15 -0
  27. package/build/vue/index.d.ts +1 -0
  28. package/build/vue/index.js +1 -0
  29. package/build-ngc/angular/clipboard-paste-button/index.d.ts +18 -0
  30. package/build-ngc/angular/clipboard-paste-button/index.js +58 -0
  31. package/build-ngc/angular/clipboard-paste-button/index.js.map +1 -0
  32. package/build-ngc/core/clipboard-paste-button.d.ts +29 -0
  33. package/build-ngc/core/clipboard-paste-button.js +51 -0
  34. package/build-ngc/core/clipboard-paste-button.js.map +1 -0
  35. package/build-ngc/core/clipboard.d.ts +2 -0
  36. package/build-ngc/core/clipboard.js +6 -1
  37. package/build-ngc/core/clipboard.js.map +1 -1
  38. package/build-ngc/core/index.d.ts +3 -2
  39. package/build-ngc/core/index.js +2 -1
  40. package/build-ngc/core/index.js.map +1 -1
  41. package/build-ngc/core/native-module.d.ts +1 -0
  42. package/build-ngc/core/native-module.js.map +1 -1
  43. package/build-ngc/core/types.d.ts +11 -0
  44. package/build-ngc/core/types.js +1 -4
  45. package/build-ngc/core/types.js.map +1 -1
  46. package/package.json +17 -19
  47. package/src/angular/clipboard-paste-button/index.ts +40 -0
  48. package/src/core/clipboard-paste-button.ts +113 -0
  49. package/src/core/clipboard.ts +7 -1
  50. package/src/core/index.ts +14 -0
  51. package/src/core/native-module.ts +2 -0
  52. package/src/core/types.ts +19 -4
  53. package/src/react/clipboard-paste-button.ts +12 -0
  54. package/src/react/index.ts +1 -0
  55. package/src/solid/clipboard-paste-button.ts +15 -0
  56. package/src/solid/index.ts +1 -0
  57. package/src/svelte/clipboard-paste-button.svelte +17 -0
  58. package/src/svelte/index.ts +1 -0
  59. package/src/svelte/svelte-compile.test-helper.ts +6 -0
  60. package/src/vue/clipboard-paste-button.ts +16 -0
  61. package/src/vue/index.ts +1 -0
@@ -0,0 +1,18 @@
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
+ import * as i0 from "@angular/core";
5
+ /** Angular twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
6
+ export declare class ClipboardPasteButton extends NativeViewBase {
7
+ onPress: IClipboardPasteButtonProps['onPress'];
8
+ backgroundColor?: IClipboardPasteButtonProps['backgroundColor'];
9
+ foregroundColor?: IClipboardPasteButtonProps['foregroundColor'];
10
+ cornerStyle?: IClipboardPasteButtonProps['cornerStyle'];
11
+ displayMode?: IClipboardPasteButtonProps['displayMode'];
12
+ imageOptions?: IClipboardPasteButtonProps['imageOptions'];
13
+ acceptedContentTypes?: IClipboardPasteButtonProps['acceptedContentTypes'];
14
+ protected readonly propNames: readonly ["onPress", "backgroundColor", "foregroundColor", "cornerStyle", "displayMode", "imageOptions", "acceptedContentTypes"];
15
+ protected renderView(props: object): IDescriptor | null;
16
+ static ɵfac: i0.ɵɵFactoryDeclaration<ClipboardPasteButton, never>;
17
+ static ɵcmp: i0.ɵɵComponentDeclaration<ClipboardPasteButton, "ClipboardPasteButton", never, { "onPress": { "alias": "onPress"; "required": true; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; }; "foregroundColor": { "alias": "foregroundColor"; "required": false; }; "cornerStyle": { "alias": "cornerStyle"; "required": false; }; "displayMode": { "alias": "displayMode"; "required": false; }; "imageOptions": { "alias": "imageOptions"; "required": false; }; "acceptedContentTypes": { "alias": "acceptedContentTypes"; "required": false; }; }, {}, never, never, true, never>;
18
+ }
@@ -0,0 +1,58 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+ import { DescriptorOutlet, NativeViewBase } from '@symbiote-native/angular';
3
+ import { renderClipboardPasteButton } from '../../core/clipboard-paste-button';
4
+ import * as i0 from "@angular/core";
5
+ /** Angular twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
6
+ export class ClipboardPasteButton extends NativeViewBase {
7
+ onPress;
8
+ backgroundColor;
9
+ foregroundColor;
10
+ cornerStyle;
11
+ displayMode;
12
+ imageOptions;
13
+ acceptedContentTypes;
14
+ propNames = [
15
+ 'onPress',
16
+ 'backgroundColor',
17
+ 'foregroundColor',
18
+ 'cornerStyle',
19
+ 'displayMode',
20
+ 'imageOptions',
21
+ 'acceptedContentTypes',
22
+ ];
23
+ renderView(props) {
24
+ return renderClipboardPasteButton(props);
25
+ }
26
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ClipboardPasteButton, deps: null, target: i0.ɵɵFactoryTarget.Component });
27
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ClipboardPasteButton, isStandalone: true, selector: "ClipboardPasteButton", inputs: { onPress: "onPress", backgroundColor: "backgroundColor", foregroundColor: "foregroundColor", cornerStyle: "cornerStyle", displayMode: "displayMode", imageOptions: "imageOptions", acceptedContentTypes: "acceptedContentTypes" }, usesInheritance: true, ngImport: i0, template: `@if (descriptor; as node) {
28
+ <symbiote-descriptor-outlet [node]="node" />
29
+ }`, isInline: true, dependencies: [{ kind: "component", type: DescriptorOutlet, selector: "symbiote-descriptor-outlet", inputs: ["node"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
30
+ }
31
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ClipboardPasteButton, decorators: [{
32
+ type: Component,
33
+ args: [{
34
+ selector: 'ClipboardPasteButton',
35
+ standalone: true,
36
+ imports: [DescriptorOutlet],
37
+ changeDetection: ChangeDetectionStrategy.OnPush,
38
+ template: `@if (descriptor; as node) {
39
+ <symbiote-descriptor-outlet [node]="node" />
40
+ }`,
41
+ }]
42
+ }], propDecorators: { onPress: [{
43
+ type: Input,
44
+ args: [{ required: true }]
45
+ }], backgroundColor: [{
46
+ type: Input
47
+ }], foregroundColor: [{
48
+ type: Input
49
+ }], cornerStyle: [{
50
+ type: Input
51
+ }], displayMode: [{
52
+ type: Input
53
+ }], imageOptions: [{
54
+ type: Input
55
+ }], acceptedContentTypes: [{
56
+ type: Input
57
+ }] } });
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/angular/clipboard-paste-button/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;;AAG/E,6FAA6F;AAU7F,MAAM,OAAO,oBAAqB,SAAQ,cAAc;IAC3B,OAAO,CAAyC;IAClE,eAAe,CAAiD;IAChE,eAAe,CAAiD;IAChE,WAAW,CAA6C;IACxD,WAAW,CAA6C;IACxD,YAAY,CAA8C;IAEnE,oBAAoB,CAAsD;IAE9C,SAAS,GAAG;QACtC,SAAS;QACT,iBAAiB;QACjB,iBAAiB;QACjB,aAAa;QACb,aAAa;QACb,cAAc;QACd,sBAAsB;KACoC,CAAC;IAE1C,UAAU,CAAC,KAAa;QACzC,OAAO,0BAA0B,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;uGAtBU,oBAAoB;2FAApB,oBAAoB,mVAJrB;;IAER,4DAJQ,gBAAgB;;2FAMf,oBAAoB;kBAThC,SAAS;mBAAC;oBACT,QAAQ,EAAE,sBAAsB;oBAChC,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,gBAAgB,CAAC;oBAC3B,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,QAAQ,EAAE;;IAER;iBACH;;sBAEE,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;;sBACxB,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK"}
@@ -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,51 @@
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
+ }
51
+ //# sourceMappingURL=clipboard-paste-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-paste-button.js","sourceRoot":"","sources":["../../src/core/clipboard-paste-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAC;AAOjD,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAejC,MAAM,CAAC,MAAM,kCAAkC,GAAG,eAAe,CAAC;AAElE,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAE1C,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAW,CAAC;AA4B7D,MAAM,UAAU,4BAA4B;IAC1C,OAAO,mBAAmB,CAAC,kCAAkC,CAAC,CAAC;AACjE,CAAC;AAED,mGAAmG;AACnG,iGAAiG;AACjG,mFAAmF;AACnF,MAAM,UAAU,oCAAoC;IAClD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,OAAO,qBAAqB,CAAC,GAAG,EAAE,CAChC,wBAAwB,CAAC,kCAAkC,CAAC,CAC7D,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,QAAQ;QAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,QAAQ,CACjD,CAAC;AACJ,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,mBAAmB,CACjC,KAAqB;IAErB,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC;IACrD,IAAI,IAAI,KAAK,UAAU,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QACtD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,IACE,IAAI,KAAK,UAAU,CAAC,KAAK;QACzB,OAAO,IAAI,KAAK,QAAQ;QACxB,WAAW,CAAC,IAAI,CAAC,EACjB,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACtD,IAAI,CAAC,oCAAoC,EAAE;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,OAAO,GAAY,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAClC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC,CAC3D,CAAC;IACF,OAAO,EAAE,CAAC,4BAA4B,EAAE,EAAE;QACxC,GAAG,SAAS;QACZ,CAAC,gBAAgB,CAAC,EAAE,CAAC,KAAqB,EAAQ,EAAE;YAClD,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,UAAU;gBAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACjE,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -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.
@@ -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';
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.
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/core/clipboard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/core/clipboard.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
- export { getStringAsync, setStringAsync, hasStringAsync, getUrlAsync, setUrlAsync, hasUrlAsync, getImageAsync, setImageAsync, hasImageAsync, addClipboardListener, removeClipboardListener, } from './clipboard';
2
- export { ContentType, StringFormat, type IGetStringOptions, type ISetStringOptions, type IGetImageOptions, type IClipboardImage, type IClipboardEvent, } from './types';
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';
@@ -1,3 +1,4 @@
1
- export { getStringAsync, setStringAsync, hasStringAsync, getUrlAsync, setUrlAsync, hasUrlAsync, getImageAsync, setImageAsync, hasImageAsync, addClipboardListener, removeClipboardListener, } from './clipboard';
1
+ export { getStringAsync, setStringAsync, hasStringAsync, getUrlAsync, setUrlAsync, hasUrlAsync, getImageAsync, setImageAsync, hasImageAsync, addClipboardListener, removeClipboardListener, isPasteButtonAvailable, } from './clipboard';
2
+ export { clipboardPasteButtonViewName, ensureClipboardPasteButtonRegistered, renderClipboardPasteButton, toPasteEventPayload, } from './clipboard-paste-button';
2
3
  export { ContentType, StringFormat, } from './types';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,WAAW,EACX,WAAW,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,YAAY,GAMb,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,WAAW,EACX,WAAW,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,4BAA4B,EAC5B,oCAAoC,EACpC,0BAA0B,EAC1B,mBAAmB,GAEpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,WAAW,EACX,YAAY,GAYb,MAAM,SAAS,CAAC"}
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAUxD,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAEnD,4FAA4F;AAC5F,8FAA8F;AAC9F,mBAAmB;AACnB,MAAM,CAAC,MAAM,4BAA4B,GAAG,oBAAoB,CAAC;AAwBjE,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAC9C,0BAA0B,CAC3B,CAAC"}
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAUxD,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAEnD,4FAA4F;AAC5F,8FAA8F;AAC9F,mBAAmB;AACnB,MAAM,CAAC,MAAM,4BAA4B,GAAG,oBAAoB,CAAC;AA0BjE,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAC9C,0BAA0B,CAC3B,CAAC"}
@@ -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;
@@ -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. `ClipboardPasteButton`'s own 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
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,uFAAuF;AACvF,gGAAgG;AAChG,+EAA+E;AAC/E,6FAA6F;AAC7F,gGAAgG;AAChG,0CAA0C;AAE1C;;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"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,uFAAuF;AACvF,gGAAgG;AAChG,2CAA2C;AAE3C;;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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/clipboard",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "expo-clipboard wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Reads and writes clipboard text, URLs, and images, plus a clipboard-change listener.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -42,10 +42,7 @@
42
42
  "types": "./build/react/index.d.ts",
43
43
  "default": "./build/react/index.js"
44
44
  },
45
- "./svelte": {
46
- "types": "./build/svelte/index.d.ts",
47
- "default": "./build/svelte/index.js"
48
- },
45
+ "./svelte": "./src/svelte/index.ts",
49
46
  "./solid": {
50
47
  "types": "./build/solid/index.d.ts",
51
48
  "default": "./build/solid/index.js"
@@ -70,7 +67,8 @@
70
67
  },
71
68
  "dependencies": {
72
69
  "expo-clipboard": "57.0.1",
73
- "expo-modules-core": "57.0.5"
70
+ "expo-modules-core": "57.0.20",
71
+ "@symbiote-native/components": "3.1.4"
74
72
  },
75
73
  "peerDependencies": {
76
74
  "@angular/core": ">=20",
@@ -80,12 +78,12 @@
80
78
  "solid-js": ">=1.9.0",
81
79
  "svelte": ">=5.56.0",
82
80
  "vue": ">=3.5.0",
83
- "@symbiote-native/angular": "^3.1.1",
84
- "@symbiote-native/engine": "^1.3.0",
85
- "@symbiote-native/react": "^3.0.3",
86
- "@symbiote-native/solid": "^3.0.3",
87
- "@symbiote-native/svelte": "^3.0.3",
88
- "@symbiote-native/vue": "^3.0.3"
81
+ "@symbiote-native/angular": "^3.2.0",
82
+ "@symbiote-native/engine": "^1.5.0",
83
+ "@symbiote-native/react": "^3.2.0",
84
+ "@symbiote-native/solid": "^3.1.0",
85
+ "@symbiote-native/svelte": "^3.1.0",
86
+ "@symbiote-native/vue": "^3.2.0"
89
87
  },
90
88
  "peerDependenciesMeta": {
91
89
  "@symbiote-native/angular": {
@@ -133,13 +131,13 @@
133
131
  "solid-js": "^1.9.14",
134
132
  "svelte": "^5.56.0",
135
133
  "typescript": "~6.0.0",
136
- "@symbiote-native/angular": "3.1.1",
137
- "@symbiote-native/engine": "1.3.0",
138
- "@symbiote-native/react": "3.0.3",
139
- "@symbiote-native/solid": "3.0.3",
140
- "@symbiote-native/svelte": "3.0.3",
141
- "@symbiote-native/test-utils": "0.4.3",
142
- "@symbiote-native/vue": "3.0.3"
134
+ "@symbiote-native/angular": "3.2.0",
135
+ "@symbiote-native/engine": "1.5.0",
136
+ "@symbiote-native/react": "3.2.0",
137
+ "@symbiote-native/solid": "3.1.0",
138
+ "@symbiote-native/svelte": "3.1.0",
139
+ "@symbiote-native/test-utils": "0.4.6",
140
+ "@symbiote-native/vue": "3.2.0"
143
141
  },
144
142
  "scripts": {
145
143
  "typecheck": "tsc --build",
@@ -0,0 +1,40 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+ import { DescriptorOutlet, NativeViewBase } from '@symbiote-native/angular';
3
+ import type { IDescriptor } from '@symbiote-native/components';
4
+ import { renderClipboardPasteButton } from '../../core/clipboard-paste-button';
5
+ import type { IClipboardPasteButtonProps } from '../../core/clipboard-paste-button';
6
+
7
+ /** Angular twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
8
+ @Component({
9
+ selector: 'ClipboardPasteButton',
10
+ standalone: true,
11
+ imports: [DescriptorOutlet],
12
+ changeDetection: ChangeDetectionStrategy.OnPush,
13
+ template: `@if (descriptor; as node) {
14
+ <symbiote-descriptor-outlet [node]="node" />
15
+ }`,
16
+ })
17
+ export class ClipboardPasteButton extends NativeViewBase {
18
+ @Input({ required: true }) onPress!: IClipboardPasteButtonProps['onPress'];
19
+ @Input() backgroundColor?: IClipboardPasteButtonProps['backgroundColor'];
20
+ @Input() foregroundColor?: IClipboardPasteButtonProps['foregroundColor'];
21
+ @Input() cornerStyle?: IClipboardPasteButtonProps['cornerStyle'];
22
+ @Input() displayMode?: IClipboardPasteButtonProps['displayMode'];
23
+ @Input() imageOptions?: IClipboardPasteButtonProps['imageOptions'];
24
+ @Input()
25
+ acceptedContentTypes?: IClipboardPasteButtonProps['acceptedContentTypes'];
26
+
27
+ protected override readonly propNames = [
28
+ 'onPress',
29
+ 'backgroundColor',
30
+ 'foregroundColor',
31
+ 'cornerStyle',
32
+ 'displayMode',
33
+ 'imageOptions',
34
+ 'acceptedContentTypes',
35
+ ] as const satisfies readonly (keyof ClipboardPasteButton)[];
36
+
37
+ protected override renderView(props: object): IDescriptor | null {
38
+ return renderClipboardPasteButton(props);
39
+ }
40
+ }
@@ -0,0 +1,113 @@
1
+ import { Platform, requireNativeViewManager } from 'expo-modules-core';
2
+ import { el } from '@symbiote-native/components';
3
+ import type {
4
+ IAccessibilityProps,
5
+ IAriaProps,
6
+ IDescriptor,
7
+ IResponderProps,
8
+ } from '@symbiote-native/components';
9
+ import {
10
+ expoViewManagerName,
11
+ tryRegisterNativeView,
12
+ } from '@symbiote-native/engine';
13
+ import type {
14
+ IStyleProp,
15
+ ISymbioteEvent,
16
+ IViewStyle,
17
+ } from '@symbiote-native/engine';
18
+ import type {
19
+ IAcceptedContentType,
20
+ IClipboardImage,
21
+ ICornerStyle,
22
+ IDisplayMode,
23
+ IGetImageOptions,
24
+ IPasteEventPayload,
25
+ } from './types';
26
+
27
+ export const CLIPBOARD_PASTE_BUTTON_MODULE_NAME = 'ExpoClipboard';
28
+
29
+ const PASTE_EVENT_PROP = 'onPastePressed';
30
+
31
+ const PASTE_TYPE = { text: 'text', image: 'image' } as const;
32
+
33
+ // Apple restricts the control: its colors, corners and label come from the props below, not `style`
34
+ export type IClipboardPasteButtonProps = IAccessibilityProps &
35
+ IAriaProps &
36
+ IResponderProps & {
37
+ /** Called with the pasted text or image, inspect `type` to tell them apart */
38
+ onPress: (data: IPasteEventPayload) => void;
39
+ /** Leaving it out lets the color follow the system theme */
40
+ backgroundColor?: string | null;
41
+ /** @default 'white' */
42
+ foregroundColor?: string | null;
43
+ /** @default 'capsule' */
44
+ cornerStyle?: ICornerStyle | null;
45
+ /** @default 'iconAndLabel' */
46
+ displayMode?: IDisplayMode | null;
47
+ /** Needs a width and a height, or the button does not appear */
48
+ style?: IStyleProp<
49
+ Omit<IViewStyle, 'backgroundColor' | 'borderRadius' | 'color'>
50
+ >;
51
+ imageOptions?: IGetImageOptions | null;
52
+ /** Do not combine `plain-text` with `html`, all text would be treated as `html` */
53
+ acceptedContentTypes?: IAcceptedContentType[];
54
+ testID?: string;
55
+ nativeID?: string;
56
+ onLayout?: (event: ISymbioteEvent) => void;
57
+ };
58
+
59
+ export function clipboardPasteButtonViewName(): string {
60
+ return expoViewManagerName(CLIPBOARD_PASTE_BUTTON_MODULE_NAME);
61
+ }
62
+
63
+ // Registers the view config in React Native's registry, which the engine derives the view's events
64
+ // and prop processors from. Called at render, not at import: a registration that only a barrel's
65
+ // load-time side effect performs is dropped by Metro's `inlineRequires` in release
66
+ export function ensureClipboardPasteButtonRegistered(): boolean {
67
+ if (!Platform.select({ ios: true, default: false })) return false;
68
+ return tryRegisterNativeView(() =>
69
+ requireNativeViewManager(CLIPBOARD_PASTE_BUTTON_MODULE_NAME),
70
+ );
71
+ }
72
+
73
+ function isImageSize(value: unknown): value is IClipboardImage['size'] {
74
+ return (
75
+ typeof value === 'object' &&
76
+ value !== null &&
77
+ typeof Reflect.get(value, 'width') === 'number' &&
78
+ typeof Reflect.get(value, 'height') === 'number'
79
+ );
80
+ }
81
+
82
+ /** Reads the native paste event, `null` when its payload has neither a text nor an image shape */
83
+ export function toPasteEventPayload(
84
+ event: ISymbioteEvent,
85
+ ): IPasteEventPayload | null {
86
+ const { type, text, data, size } = event.nativeEvent;
87
+ if (type === PASTE_TYPE.text && typeof text === 'string')
88
+ return { type, text };
89
+ if (
90
+ type === PASTE_TYPE.image &&
91
+ typeof data === 'string' &&
92
+ isImageSize(size)
93
+ ) {
94
+ return { type, data, size };
95
+ }
96
+ return null;
97
+ }
98
+
99
+ // `object`, not the props type: Vue and Angular hand over an untyped attribute bag
100
+ export function renderClipboardPasteButton(props: object): IDescriptor | null {
101
+ if (!ensureClipboardPasteButtonRegistered()) return null;
102
+ const onPress: unknown = Reflect.get(props, 'onPress');
103
+ const viewProps = Object.fromEntries(
104
+ Object.entries(props).filter(([key]) => key !== 'onPress'),
105
+ );
106
+ return el(clipboardPasteButtonViewName(), {
107
+ ...viewProps,
108
+ [PASTE_EVENT_PROP]: (event: ISymbioteEvent): void => {
109
+ const payload = toPasteEventPayload(event);
110
+ if (payload && typeof onPress === 'function') onPress(payload);
111
+ },
112
+ });
113
+ }
@@ -1,4 +1,4 @@
1
- import { UnavailabilityError } from 'expo-modules-core';
1
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
2
2
  import type { EventSubscription } from 'expo-modules-core';
3
3
 
4
4
  import { CLIPBOARD_CHANGED_EVENT_NAME, expoClipboard } from './native-module';
@@ -163,6 +163,12 @@ export function addClipboardListener(
163
163
  return expoClipboard.addListener(CLIPBOARD_CHANGED_EVENT_NAME, listener);
164
164
  }
165
165
 
166
+ /** Whether `ClipboardPasteButton` can render, needs iOS 16 or newer */
167
+ export const isPasteButtonAvailable: boolean = Platform.select({
168
+ ios: expoClipboard.isPasteButtonAvailable ?? false,
169
+ default: false,
170
+ });
171
+
166
172
  /**
167
173
  * Removes the listener added by `addClipboardListener`.
168
174
  * @deprecated use `subscription.remove()` instead.
package/src/core/index.ts CHANGED
@@ -10,7 +10,15 @@ export {
10
10
  hasImageAsync,
11
11
  addClipboardListener,
12
12
  removeClipboardListener,
13
+ isPasteButtonAvailable,
13
14
  } from './clipboard';
15
+ export {
16
+ clipboardPasteButtonViewName,
17
+ ensureClipboardPasteButtonRegistered,
18
+ renderClipboardPasteButton,
19
+ toPasteEventPayload,
20
+ type IClipboardPasteButtonProps,
21
+ } from './clipboard-paste-button';
14
22
  export {
15
23
  ContentType,
16
24
  StringFormat,
@@ -19,5 +27,11 @@ export {
19
27
  type IGetImageOptions,
20
28
  type IClipboardImage,
21
29
  type IClipboardEvent,
30
+ type IAcceptedContentType,
31
+ type ICornerStyle,
32
+ type IDisplayMode,
33
+ type ITextPasteEvent,
34
+ type IImagePasteEvent,
35
+ type IPasteEventPayload,
22
36
  } from './types';
23
37
  export type { EventSubscription } from 'expo-modules-core';
@@ -35,6 +35,8 @@ export type INativeClipboardModule = {
35
35
  getImageAsync?(options: IGetImageOptions): Promise<IClipboardImage | null>;
36
36
  setImageAsync?(base64Image: string): Promise<void>;
37
37
  hasImageAsync?(): Promise<boolean>;
38
+ // iOS 16+ only, absent elsewhere
39
+ isPasteButtonAvailable?: boolean;
38
40
  };
39
41
 
40
42
  export const expoClipboard = requireNativeModule<INativeClipboardModule>(
package/src/core/types.ts 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. `ClipboardPasteButton`'s own 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
  /**
10
7
  * Type used to define what type of data is stored in the clipboard.
@@ -79,3 +76,21 @@ export type IClipboardEvent = {
79
76
  */
80
77
  contentTypes: ContentType[];
81
78
  };
79
+
80
+ export type IAcceptedContentType = 'plain-text' | 'image' | 'url' | 'html';
81
+
82
+ export type ICornerStyle =
83
+ 'dynamic' | 'fixed' | 'capsule' | 'large' | 'medium' | 'small';
84
+
85
+ export type IDisplayMode = 'iconAndLabel' | 'iconOnly' | 'labelOnly';
86
+
87
+ export type ITextPasteEvent = {
88
+ text: string;
89
+ type: 'text';
90
+ };
91
+
92
+ export type IImagePasteEvent = {
93
+ type: 'image';
94
+ } & IClipboardImage;
95
+
96
+ export type IPasteEventPayload = ITextPasteEvent | IImagePasteEvent;
@@ -0,0 +1,12 @@
1
+ import type { ReactElement } from 'react';
2
+ import { descriptorToReact } from '@symbiote-native/react';
3
+ import { renderClipboardPasteButton } from '../core/clipboard-paste-button';
4
+ import type { IClipboardPasteButtonProps } from '../core/clipboard-paste-button';
5
+
6
+ /** React twin of `expo-clipboard`'s `ClipboardPasteButton` (`UIPasteControl`), iOS only */
7
+ export function ClipboardPasteButton(
8
+ props: IClipboardPasteButtonProps,
9
+ ): ReactElement | null {
10
+ const descriptor = renderClipboardPasteButton(props);
11
+ return descriptor ? descriptorToReact(descriptor) : null;
12
+ }
@@ -6,4 +6,5 @@
6
6
  // @symbiote-native/sensors' useAccelerometer.
7
7
 
8
8
  export { useClipboard } from './hooks/use-clipboard';
9
+ export { ClipboardPasteButton } from './clipboard-paste-button';
9
10
  export * from '../core';
@@ -0,0 +1,15 @@
1
+ import { descriptorToSolid } from '@symbiote-native/solid';
2
+ import { renderClipboardPasteButton } from '../core/clipboard-paste-button';
3
+ import type { IClipboardPasteButtonProps } from '../core/clipboard-paste-button';
4
+
5
+ // The platform decision is made once at mount: the descriptor bridge builds the node once and
6
+ // only diffs prop values afterwards, so the descriptor's shape must not change
7
+ export function ClipboardPasteButton(
8
+ props: IClipboardPasteButtonProps,
9
+ ): ReturnType<typeof descriptorToSolid> | null {
10
+ const initial = renderClipboardPasteButton({ ...props });
11
+ if (!initial) return null;
12
+ return descriptorToSolid(
13
+ () => renderClipboardPasteButton({ ...props }) ?? initial,
14
+ );
15
+ }