@embedpdf/plugin-selection 1.5.0 → 2.0.0-next.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/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +445 -166
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +61 -24
- package/dist/lib/reducer.d.ts +2 -1
- package/dist/lib/selection-plugin.d.ts +25 -5
- package/dist/lib/selectors.d.ts +7 -7
- package/dist/lib/types.d.ts +62 -9
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +88 -34
- package/dist/preact/index.js.map +1 -1
- package/dist/preact/utils.d.ts +1 -0
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +88 -34
- package/dist/react/index.js.map +1 -1
- package/dist/react/utils.d.ts +1 -0
- package/dist/shared/components/selection-layer.d.ts +7 -2
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/types.d.ts +7 -0
- package/dist/shared-preact/components/selection-layer.d.ts +7 -2
- package/dist/shared-preact/index.d.ts +1 -0
- package/dist/shared-preact/types.d.ts +7 -0
- package/dist/shared-react/components/selection-layer.d.ts +7 -2
- package/dist/shared-react/index.d.ts +1 -0
- package/dist/shared-react/types.d.ts +7 -0
- package/dist/svelte/components/SelectionLayer.svelte.d.ts +13 -2
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +162 -34
- package/dist/svelte/index.js.map +1 -1
- package/dist/svelte/types.d.ts +7 -0
- package/dist/vue/components/copy-to-clipboard.vue.d.ts +2 -1
- package/dist/vue/components/selection-layer.vue.d.ts +27 -3
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +137 -43
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/types.d.ts +7 -0
- package/package.json +9 -8
package/dist/vue/index.js
CHANGED
|
@@ -1,64 +1,158 @@
|
|
|
1
1
|
import { createPluginPackage } from "@embedpdf/core";
|
|
2
2
|
import { SelectionPlugin, SelectionPluginPackage as SelectionPluginPackage$1 } from "@embedpdf/plugin-selection";
|
|
3
3
|
export * from "@embedpdf/plugin-selection";
|
|
4
|
-
import { defineComponent, ref,
|
|
5
|
-
import { useCapability,
|
|
4
|
+
import { defineComponent, useSlots, ref, computed, watch, createElementBlock, createCommentVNode, openBlock, Fragment, createElementVNode, createBlock, normalizeStyle, renderList, unref, withCtx, renderSlot, resolveDynamicComponent, watchEffect } from "vue";
|
|
5
|
+
import { usePlugin, useCapability, useDocumentState } from "@embedpdf/core/vue";
|
|
6
|
+
import { Rotation } from "@embedpdf/models";
|
|
7
|
+
import { CounterRotate } from "@embedpdf/utils/vue";
|
|
6
8
|
const useSelectionCapability = () => useCapability(SelectionPlugin.id);
|
|
7
9
|
const useSelectionPlugin = () => usePlugin(SelectionPlugin.id);
|
|
8
10
|
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
9
11
|
__name: "selection-layer",
|
|
10
12
|
props: {
|
|
13
|
+
documentId: {},
|
|
11
14
|
pageIndex: {},
|
|
12
15
|
scale: {},
|
|
13
|
-
|
|
16
|
+
rotation: { default: Rotation.Degree0 },
|
|
17
|
+
background: { default: "rgba(33,150,243)" },
|
|
18
|
+
selectionMenu: {}
|
|
14
19
|
},
|
|
15
20
|
setup(__props) {
|
|
16
21
|
const props = __props;
|
|
17
|
-
const
|
|
22
|
+
const slots = useSlots();
|
|
23
|
+
const { plugin: selPlugin } = useSelectionPlugin();
|
|
24
|
+
const documentState = useDocumentState(() => props.documentId);
|
|
18
25
|
const rects = ref([]);
|
|
19
26
|
const boundingRect = ref(null);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const placement = ref(null);
|
|
28
|
+
const actualScale = computed(() => {
|
|
29
|
+
var _a;
|
|
30
|
+
if (props.scale !== void 0) return props.scale;
|
|
31
|
+
return ((_a = documentState.value) == null ? void 0 : _a.scale) ?? 1;
|
|
32
|
+
});
|
|
33
|
+
const actualRotation = computed(() => {
|
|
34
|
+
var _a;
|
|
35
|
+
if (props.rotation !== void 0) return props.rotation;
|
|
36
|
+
return ((_a = documentState.value) == null ? void 0 : _a.rotation) ?? Rotation.Degree0;
|
|
30
37
|
});
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
const shouldRenderMenu = computed(() => {
|
|
39
|
+
if (!placement.value) return false;
|
|
40
|
+
if (placement.value.pageIndex !== props.pageIndex) return false;
|
|
41
|
+
if (!placement.value.isVisible) return false;
|
|
42
|
+
return !!props.selectionMenu || !!slots["selection-menu"];
|
|
43
|
+
});
|
|
44
|
+
watch(
|
|
45
|
+
[() => selPlugin.value, () => props.documentId, () => props.pageIndex],
|
|
46
|
+
([plugin, docId, pageIdx], _, onCleanup) => {
|
|
47
|
+
if (!plugin || !docId) {
|
|
48
|
+
rects.value = [];
|
|
49
|
+
boundingRect.value = null;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const unregister = plugin.registerSelectionOnPage({
|
|
53
|
+
documentId: docId,
|
|
54
|
+
pageIndex: pageIdx,
|
|
55
|
+
onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {
|
|
56
|
+
rects.value = newRects;
|
|
57
|
+
boundingRect.value = newBoundingRect;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
onCleanup(unregister);
|
|
61
|
+
},
|
|
62
|
+
{ immediate: true }
|
|
63
|
+
);
|
|
64
|
+
watch(
|
|
65
|
+
[() => selPlugin.value, () => props.documentId],
|
|
66
|
+
([plugin, docId], _, onCleanup) => {
|
|
67
|
+
if (!plugin || !docId) {
|
|
68
|
+
placement.value = null;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const unsubscribe = plugin.onMenuPlacement(docId, (newPlacement) => {
|
|
72
|
+
placement.value = newPlacement;
|
|
73
|
+
});
|
|
74
|
+
onCleanup(unsubscribe);
|
|
75
|
+
},
|
|
76
|
+
{ immediate: true }
|
|
77
|
+
);
|
|
78
|
+
const buildContext = () => ({
|
|
79
|
+
type: "selection",
|
|
80
|
+
pageIndex: props.pageIndex
|
|
33
81
|
});
|
|
82
|
+
const buildMenuPlacement = () => {
|
|
83
|
+
var _a, _b, _c;
|
|
84
|
+
return {
|
|
85
|
+
suggestTop: ((_a = placement.value) == null ? void 0 : _a.suggestTop) ?? false,
|
|
86
|
+
spaceAbove: ((_b = placement.value) == null ? void 0 : _b.spaceAbove) ?? 0,
|
|
87
|
+
spaceBelow: ((_c = placement.value) == null ? void 0 : _c.spaceBelow) ?? 0
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const renderSelectionMenu = (rect, menuWrapperProps) => {
|
|
91
|
+
if (!props.selectionMenu) return null;
|
|
92
|
+
return props.selectionMenu({
|
|
93
|
+
rect,
|
|
94
|
+
menuWrapperProps,
|
|
95
|
+
selected: true,
|
|
96
|
+
// Selection is always "selected" when visible
|
|
97
|
+
placement: buildMenuPlacement(),
|
|
98
|
+
context: buildContext()
|
|
99
|
+
});
|
|
100
|
+
};
|
|
34
101
|
return (_ctx, _cache) => {
|
|
35
|
-
return boundingRect.value ? (openBlock(), createElementBlock(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
102
|
+
return boundingRect.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
103
|
+
createElementVNode("div", {
|
|
104
|
+
style: normalizeStyle({
|
|
105
|
+
position: "absolute",
|
|
106
|
+
left: `${boundingRect.value.origin.x * actualScale.value}px`,
|
|
107
|
+
top: `${boundingRect.value.origin.y * actualScale.value}px`,
|
|
108
|
+
width: `${boundingRect.value.size.width * actualScale.value}px`,
|
|
109
|
+
height: `${boundingRect.value.size.height * actualScale.value}px`,
|
|
110
|
+
mixBlendMode: "multiply",
|
|
111
|
+
isolation: "isolate",
|
|
112
|
+
pointerEvents: "none"
|
|
113
|
+
})
|
|
114
|
+
}, [
|
|
115
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(rects.value, (rect, i) => {
|
|
116
|
+
return openBlock(), createElementBlock("div", {
|
|
117
|
+
key: i,
|
|
118
|
+
style: normalizeStyle({
|
|
119
|
+
position: "absolute",
|
|
120
|
+
left: `${(rect.origin.x - boundingRect.value.origin.x) * actualScale.value}px`,
|
|
121
|
+
top: `${(rect.origin.y - boundingRect.value.origin.y) * actualScale.value}px`,
|
|
122
|
+
width: `${rect.size.width * actualScale.value}px`,
|
|
123
|
+
height: `${rect.size.height * actualScale.value}px`,
|
|
124
|
+
background: __props.background
|
|
125
|
+
})
|
|
126
|
+
}, null, 4);
|
|
127
|
+
}), 128))
|
|
128
|
+
], 4),
|
|
129
|
+
shouldRenderMenu.value ? (openBlock(), createBlock(unref(CounterRotate), {
|
|
130
|
+
key: 0,
|
|
131
|
+
rect: {
|
|
132
|
+
origin: {
|
|
133
|
+
x: placement.value.rect.origin.x * actualScale.value,
|
|
134
|
+
y: placement.value.rect.origin.y * actualScale.value
|
|
135
|
+
},
|
|
136
|
+
size: {
|
|
137
|
+
width: placement.value.rect.size.width * actualScale.value,
|
|
138
|
+
height: placement.value.rect.size.height * actualScale.value
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
rotation: actualRotation.value
|
|
142
|
+
}, {
|
|
143
|
+
default: withCtx(({ rect, menuWrapperProps }) => [
|
|
144
|
+
__props.selectionMenu ? (openBlock(), createBlock(resolveDynamicComponent(renderSelectionMenu(rect, menuWrapperProps)), { key: 0 })) : renderSlot(_ctx.$slots, "selection-menu", {
|
|
145
|
+
key: 1,
|
|
146
|
+
context: buildContext(),
|
|
147
|
+
selected: true,
|
|
148
|
+
rect,
|
|
149
|
+
placement: buildMenuPlacement(),
|
|
150
|
+
menuWrapperProps
|
|
58
151
|
})
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
152
|
+
]),
|
|
153
|
+
_: 3
|
|
154
|
+
}, 8, ["rect", "rotation"])) : createCommentVNode("", true)
|
|
155
|
+
], 64)) : createCommentVNode("", true);
|
|
62
156
|
};
|
|
63
157
|
}
|
|
64
158
|
});
|
|
@@ -68,7 +162,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
68
162
|
const { provides: sel } = useSelectionCapability();
|
|
69
163
|
watchEffect((onCleanup) => {
|
|
70
164
|
if (sel.value) {
|
|
71
|
-
const unsubscribe = sel.value.onCopyToClipboard((text) => {
|
|
165
|
+
const unsubscribe = sel.value.onCopyToClipboard(({ text }) => {
|
|
72
166
|
navigator.clipboard.writeText(text).catch((err) => {
|
|
73
167
|
console.error("Failed to copy text to clipboard:", err);
|
|
74
168
|
});
|
package/dist/vue/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/vue/hooks/use-selection.ts","../../src/vue/components/selection-layer.vue","../../src/vue/components/copy-to-clipboard.vue","../../src/vue/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<template>\n <div\n v-if=\"boundingRect\"\n :style=\"{\n position: 'absolute',\n left: `${boundingRect.origin.x * scale}px`,\n top: `${boundingRect.origin.y * scale}px`,\n width: `${boundingRect.size.width * scale}px`,\n height: `${boundingRect.size.height * scale}px`,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }\"\n >\n <div\n v-for=\"(rect, i) in rects\"\n :key=\"i\"\n :style=\"{\n position: 'absolute',\n left: `${(rect.origin.x - boundingRect.origin.x) * scale}px`,\n top: `${(rect.origin.y - boundingRect.origin.y) * scale}px`,\n width: `${rect.size.width * scale}px`,\n height: `${rect.size.height * scale}px`,\n background: background,\n }\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted } from 'vue';\nimport type { Rect } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks/use-selection';\n\ninterface Props {\n pageIndex: number;\n scale: number;\n background?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n background: 'rgba(33, 150, 243)',\n});\n\nconst { plugin: sel } = useSelectionPlugin();\nconst rects = ref<Rect[]>([]);\nconst boundingRect = ref<Rect | null>(null);\n\nlet unregister: (() => void) | undefined;\n\nonMounted(() => {\n if (!sel.value) return;\n\n unregister = sel.value.registerSelectionOnPage({\n pageIndex: props.pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects.value = newRects;\n boundingRect.value = newBoundingRect;\n },\n });\n});\n\nonUnmounted(() => {\n unregister?.();\n});\n</script>\n","<script setup lang=\"ts\">\nimport { watchEffect } from 'vue';\nimport { useSelectionCapability } from '../hooks';\n\nconst { provides: sel } = useSelectionCapability();\n\n// This effect runs when the component is mounted and the capability is available.\n// It automatically handles unsubscribing when the component is unmounted.\nwatchEffect((onCleanup) => {\n if (sel.value) {\n const unsubscribe = sel.value.onCopyToClipboard((text) => {\n // Use the Clipboard API to write the text\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n\n // Register the cleanup function to run on unmount or re-run\n onCleanup(unsubscribe);\n }\n});\n</script>\n\n<template>\n <!-- This component renders nothing to the DOM -->\n</template>\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n"],"names":["_createElementBlock","_normalizeStyle","scale","_openBlock","_Fragment","_renderList","background","BaseSelectionPluginPackage","CopyToClipboard"],"mappings":";;;;;AAOO,MAAM,yBAAyB,MAAM,cAA+B,gBAAgB,EAAE;AAMtF,MAAM,qBAAqB,MAAM,UAA2B,gBAAgB,EAAE;;;;;;;;;AC2BrF,UAAM,QAAQ;AAId,UAAM,EAAE,QAAQ,IAAI,IAAI,mBAAmB;AACrC,UAAA,QAAQ,IAAY,EAAE;AACtB,UAAA,eAAe,IAAiB,IAAI;AAEtC,QAAA;AAEJ,cAAU,MAAM;AACV,UAAA,CAAC,IAAI,MAAO;AAEH,mBAAA,IAAI,MAAM,wBAAwB;AAAA,QAC7C,WAAW,MAAM;AAAA,QACjB,eAAe,CAAC,EAAE,OAAO,UAAU,cAAc,sBAAsB;AACrE,gBAAM,QAAQ;AACd,uBAAa,QAAQ;AAAA,QAAA;AAAA,MACvB,CACD;AAAA,IAAA,CACF;AAED,gBAAY,MAAM;AACH;AAAA,IAAA,CACd;;aA9DS,aAAY,sBADpBA,mBAyBM,OAAA;AAAA;QAvBH,OAAKC,eAAA;AAAA;UAA+C,MAAA,GAAA,aAAA,MAAa,OAAO,IAAIC,KAAK,KAAA;AAAA,UAAoB,KAAA,GAAA,aAAA,MAAa,OAAO,IAAIA,KAAK,KAAA;AAAA,UAAsB,OAAA,GAAA,aAAA,MAAa,KAAK,QAAQA,KAAK,KAAA;AAAA,UAAuB,QAAA,GAAA,aAAA,MAAa,KAAK,SAASA,KAAK,KAAA;AAAA;;;;;SAW/OC,UAAA,IAAA,GAAAH,mBAWEI,UAVoB,MAAAC,WAAA,MAAA,OAAZ,CAAA,MAAM,MAAC;8BADjBL,mBAWE,OAAA;AAAA,YATC,KAAK;AAAA,YACL,OAAKC,eAAA;AAAA;wBAAoD,KAAK,OAAO,IAAI,aAAY,MAAC,OAAO,KAAKC,KAAK,KAAA;AAAA,uBAAuB,KAAK,OAAO,IAAI,aAAY,MAAC,OAAO,KAAKA,KAAK,KAAA;AAAA,cAAwB,OAAA,GAAA,KAAK,KAAK,QAAQA,KAAK,KAAA;AAAA,cAAyB,QAAA,GAAA,KAAK,KAAK,SAASA,KAAK,KAAA;AAAA,0BAA0BI,KAAU;AAAA;;;;;;;;;;ACbvT,UAAM,EAAE,UAAU,IAAI,IAAI,uBAAuB;AAIjD,gBAAY,CAAC,cAAc;AACzB,UAAI,IAAI,OAAO;AACb,cAAM,cAAc,IAAI,MAAM,kBAAkB,CAAC,SAAS;AAExD,oBAAU,UAAU,UAAU,IAAI,EAAE,MAAM,CAAC,QAAQ;AACzC,oBAAA,MAAM,qCAAqC,GAAG;AAAA,UAAA,CACvD;AAAA,QAAA,CACF;AAGD,kBAAU,WAAW;AAAA,MAAA;AAAA,IACvB,CACD;;;;;;ACXM,MAAM,yBAAyB,oBAAoBC,wBAA0B,EACjF,WAAWC,SAAe,EAC1B,MAAM;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/vue/hooks/use-selection.ts","../../src/vue/components/selection-layer.vue","../../src/vue/components/copy-to-clipboard.vue","../../src/vue/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<script setup lang=\"ts\">\nimport { ref, watch, computed, useSlots, type VNode } from 'vue';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { Rotation, type Rect } from '@embedpdf/models';\nimport type { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { CounterRotate, type MenuWrapperProps } from '@embedpdf/utils/vue';\nimport { useSelectionPlugin } from '../hooks/use-selection';\nimport type { SelectionSelectionContext, SelectionSelectionMenuRenderFn } from '../types';\n\ninterface SelectionLayerProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n}\n\nconst props = withDefaults(defineProps<SelectionLayerProps>(), {\n background: 'rgba(33,150,243)',\n rotation: Rotation.Degree0,\n});\n\nconst slots = useSlots();\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst rects = ref<Rect[]>([]);\nconst boundingRect = ref<Rect | null>(null);\nconst placement = ref<SelectionMenuPlacement | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nconst actualRotation = computed(() => {\n if (props.rotation !== undefined) return props.rotation;\n return documentState.value?.rotation ?? Rotation.Degree0;\n});\n\n// Check if menu should render: placement is valid AND (render fn OR slot exists)\nconst shouldRenderMenu = computed(() => {\n if (!placement.value) return false;\n if (placement.value.pageIndex !== props.pageIndex) return false;\n if (!placement.value.isVisible) return false;\n\n // Must have either render function or slot\n return !!props.selectionMenu || !!slots['selection-menu'];\n});\n\nwatch(\n [() => selPlugin.value, () => props.documentId, () => props.pageIndex],\n ([plugin, docId, pageIdx], _, onCleanup) => {\n if (!plugin || !docId) {\n rects.value = [];\n boundingRect.value = null;\n return;\n }\n\n const unregister = plugin.registerSelectionOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects.value = newRects;\n boundingRect.value = newBoundingRect;\n },\n });\n\n onCleanup(unregister);\n },\n { immediate: true },\n);\n\nwatch(\n [() => selPlugin.value, () => props.documentId],\n ([plugin, docId], _, onCleanup) => {\n if (!plugin || !docId) {\n placement.value = null;\n return;\n }\n\n const unsubscribe = plugin.onMenuPlacement(docId, (newPlacement) => {\n placement.value = newPlacement;\n });\n\n onCleanup(unsubscribe);\n },\n { immediate: true },\n);\n\n// --- Selection Menu Logic ---\n\n// Build context object for selection menu\nconst buildContext = (): SelectionSelectionContext => ({\n type: 'selection',\n pageIndex: props.pageIndex,\n});\n\n// Build placement hints from plugin placement data\nconst buildMenuPlacement = () => ({\n suggestTop: placement.value?.suggestTop ?? false,\n spaceAbove: placement.value?.spaceAbove ?? 0,\n spaceBelow: placement.value?.spaceBelow ?? 0,\n});\n\n// Render via function (for schema-driven approach)\nconst renderSelectionMenu = (rect: Rect, menuWrapperProps: MenuWrapperProps): VNode | null => {\n if (!props.selectionMenu) return null;\n\n return props.selectionMenu({\n rect,\n menuWrapperProps,\n selected: true, // Selection is always \"selected\" when visible\n placement: buildMenuPlacement(),\n context: buildContext(),\n });\n};\n</script>\n\n<template>\n <template v-if=\"boundingRect\">\n <div\n :style=\"{\n position: 'absolute',\n left: `${boundingRect.origin.x * actualScale}px`,\n top: `${boundingRect.origin.y * actualScale}px`,\n width: `${boundingRect.size.width * actualScale}px`,\n height: `${boundingRect.size.height * actualScale}px`,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }\"\n >\n <div\n v-for=\"(rect, i) in rects\"\n :key=\"i\"\n :style=\"{\n position: 'absolute',\n left: `${(rect.origin.x - boundingRect.origin.x) * actualScale}px`,\n top: `${(rect.origin.y - boundingRect.origin.y) * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n background: background,\n }\"\n />\n </div>\n\n <!-- Selection Menu: Supports BOTH render function and slot -->\n <CounterRotate\n v-if=\"shouldRenderMenu\"\n :rect=\"{\n origin: {\n x: placement!.rect.origin.x * actualScale,\n y: placement!.rect.origin.y * actualScale,\n },\n size: {\n width: placement!.rect.size.width * actualScale,\n height: placement!.rect.size.height * actualScale,\n },\n }\"\n :rotation=\"actualRotation\"\n >\n <template #default=\"{ rect, menuWrapperProps }\">\n <!-- Priority 1: Render function prop (schema-driven) -->\n <component v-if=\"selectionMenu\" :is=\"renderSelectionMenu(rect, menuWrapperProps)\" />\n\n <!-- Priority 2: Slot (manual customization) -->\n <slot\n v-else\n name=\"selection-menu\"\n :context=\"buildContext()\"\n :selected=\"true\"\n :rect=\"rect\"\n :placement=\"buildMenuPlacement()\"\n :menuWrapperProps=\"menuWrapperProps\"\n />\n </template>\n </CounterRotate>\n </template>\n</template>\n","<script setup lang=\"ts\">\nimport { watchEffect } from 'vue';\nimport { useSelectionCapability } from '../hooks';\n\nconst { provides: sel } = useSelectionCapability();\n\n// This effect runs when the component is mounted and the capability is available.\n// It automatically handles unsubscribing when the component is unmounted.\nwatchEffect((onCleanup) => {\n if (sel.value) {\n const unsubscribe = sel.value.onCopyToClipboard(({ text }) => {\n // Use the Clipboard API to write the text\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n\n // Register the cleanup function to run on unmount or re-run\n onCleanup(unsubscribe);\n }\n});\n</script>\n\n<template>\n <!-- This component renders nothing to the DOM -->\n</template>\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from './types';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n"],"names":["_createElementBlock","_Fragment","_createElementVNode","_normalizeStyle","_openBlock","_renderList","_createBlock","_unref","_withCtx","_resolveDynamicComponent","_renderSlot","BaseSelectionPluginPackage","CopyToClipboard"],"mappings":";;;;;;;AAOO,MAAM,yBAAyB,MAAM,cAA+B,gBAAgB,EAAE;AAMtF,MAAM,qBAAqB,MAAM,UAA2B,gBAAgB,EAAE;;;;;;;;;;;;ACMrF,UAAM,QAAQ;AAKd,UAAM,QAAQ,SAAA;AACd,UAAM,EAAE,QAAQ,UAAA,IAAc,mBAAA;AAC9B,UAAM,gBAAgB,iBAAiB,MAAM,MAAM,UAAU;AAC7D,UAAM,QAAQ,IAAY,EAAE;AAC5B,UAAM,eAAe,IAAiB,IAAI;AAC1C,UAAM,YAAY,IAAmC,IAAI;AAEzD,UAAM,cAAc,SAAS,MAAM;;AACjC,UAAI,MAAM,UAAU,OAAW,QAAO,MAAM;AAC5C,eAAO,mBAAc,UAAd,mBAAqB,UAAS;AAAA,IACvC,CAAC;AAED,UAAM,iBAAiB,SAAS,MAAM;;AACpC,UAAI,MAAM,aAAa,OAAW,QAAO,MAAM;AAC/C,eAAO,mBAAc,UAAd,mBAAqB,aAAY,SAAS;AAAA,IACnD,CAAC;AAGD,UAAM,mBAAmB,SAAS,MAAM;AACtC,UAAI,CAAC,UAAU,MAAO,QAAO;AAC7B,UAAI,UAAU,MAAM,cAAc,MAAM,UAAW,QAAO;AAC1D,UAAI,CAAC,UAAU,MAAM,UAAW,QAAO;AAGvC,aAAO,CAAC,CAAC,MAAM,iBAAiB,CAAC,CAAC,MAAM,gBAAgB;AAAA,IAC1D,CAAC;AAED;AAAA,MACE,CAAC,MAAM,UAAU,OAAO,MAAM,MAAM,YAAY,MAAM,MAAM,SAAS;AAAA,MACrE,CAAC,CAAC,QAAQ,OAAO,OAAO,GAAG,GAAG,cAAc;AAC1C,YAAI,CAAC,UAAU,CAAC,OAAO;AACrB,gBAAM,QAAQ,CAAA;AACd,uBAAa,QAAQ;AACrB;AAAA,QACF;AAEA,cAAM,aAAa,OAAO,wBAAwB;AAAA,UAChD,YAAY;AAAA,UACZ,WAAW;AAAA,UACX,eAAe,CAAC,EAAE,OAAO,UAAU,cAAc,sBAAsB;AACrE,kBAAM,QAAQ;AACd,yBAAa,QAAQ;AAAA,UACvB;AAAA,QAAA,CACD;AAED,kBAAU,UAAU;AAAA,MACtB;AAAA,MACA,EAAE,WAAW,KAAA;AAAA,IAAK;AAGpB;AAAA,MACE,CAAC,MAAM,UAAU,OAAO,MAAM,MAAM,UAAU;AAAA,MAC9C,CAAC,CAAC,QAAQ,KAAK,GAAG,GAAG,cAAc;AACjC,YAAI,CAAC,UAAU,CAAC,OAAO;AACrB,oBAAU,QAAQ;AAClB;AAAA,QACF;AAEA,cAAM,cAAc,OAAO,gBAAgB,OAAO,CAAC,iBAAiB;AAClE,oBAAU,QAAQ;AAAA,QACpB,CAAC;AAED,kBAAU,WAAW;AAAA,MACvB;AAAA,MACA,EAAE,WAAW,KAAA;AAAA,IAAK;AAMpB,UAAM,eAAe,OAAkC;AAAA,MACrD,MAAM;AAAA,MACN,WAAW,MAAM;AAAA,IAAA;AAInB,UAAM,qBAAqB,MAAA;;AAAO;AAAA,QAChC,cAAY,eAAU,UAAV,mBAAiB,eAAc;AAAA,QAC3C,cAAY,eAAU,UAAV,mBAAiB,eAAc;AAAA,QAC3C,cAAY,eAAU,UAAV,mBAAiB,eAAc;AAAA,MAAA;AAAA;AAI7C,UAAM,sBAAsB,CAAC,MAAY,qBAAqD;AAC5F,UAAI,CAAC,MAAM,cAAe,QAAO;AAEjC,aAAO,MAAM,cAAc;AAAA,QACzB;AAAA,QACA;AAAA,QACA,UAAU;AAAA;AAAA,QACV,WAAW,mBAAA;AAAA,QACX,SAAS,aAAA;AAAA,MAAa,CACvB;AAAA,IACH;;aAIkB,aAAA,sBAAhBA,mBA0DWC,UAAA,EAAA,KAAA,KAAA;AAAA,QAzDTC,mBAwBM,OAAA;AAAA,UAvBH,OAAKC,eAAA;AAAA;YAAmD,MAAA,GAAA,aAAA,MAAa,OAAO,IAAI,YAAA,KAAW;AAAA,YAAsB,KAAA,GAAA,aAAA,MAAa,OAAO,IAAI,YAAA,KAAW;AAAA,YAAwB,OAAA,GAAA,aAAA,MAAa,KAAK,QAAQ,YAAA,KAAW;AAAA,YAAyB,QAAA,GAAA,aAAA,MAAa,KAAK,SAAS,YAAA,KAAW;AAAA;;;;;WAWjRC,UAAA,IAAA,GAAAJ,mBAWEC,UAAA,MAAAI,WAVoB,MAAA,OAAK,CAAjB,MAAM,MAAC;gCADjBL,mBAWE,OAAA;AAAA,cATC,KAAK;AAAA,cACL,OAAKG,eAAA;AAAA;0BAAwD,KAAK,OAAO,IAAI,aAAA,MAAa,OAAO,KAAK,YAAA,KAAW;AAAA,yBAAyB,KAAK,OAAO,IAAI,aAAA,MAAa,OAAO,KAAK,YAAA,KAAW;AAAA,gBAA0B,OAAA,GAAA,KAAK,KAAK,QAAQ,YAAA,KAAW;AAAA,gBAA2B,QAAA,GAAA,KAAK,KAAK,SAAS,YAAA,KAAW;AAAA,4BAA4B,QAAA;AAAA,cAAA;;;;QAavU,iBAAA,sBADRG,YA6BgBC,MAAA,aAAA,GAAA;AAAA;UA3Bb,MAAI;AAAA;cAAmC,GAAA,UAAA,MAAW,KAAK,OAAO,IAAI,YAAA;AAAA,cAA0B,GAAA,UAAA,MAAW,KAAK,OAAO,IAAI,YAAA;AAAA,YAAA;AAAA;cAAyD,OAAA,UAAA,MAAW,KAAK,KAAK,QAAQ,YAAA;AAAA,cAA+B,QAAA,UAAA,MAAW,KAAK,KAAK,SAAS,YAAA;AAAA,YAAA;AAAA;UAU1Q,UAAU,eAAA;AAAA,QAAA;UAEA,SAAOC,QAEhB,CAAoF,EAFhE,MAAM,uBAAgB;AAAA,YAEzB,QAAA,iBAAjBJ,UAAA,GAAAE,YAAoFG,wBAA/C,oBAAoB,MAAM,gBAAgB,CAAA,GAAA,EAAA,KAAA,EAAA,CAAA,KAG/EC,WAQE,KAAA,QAAA,kBAAA;AAAA;cALC,SAAS,aAAA;AAAA,cACT,UAAU;AAAA,cACV;AAAA,cACA,WAAW,mBAAA;AAAA,cACX;AAAA,YAAA;;;;;;;;;;;AC3KX,UAAM,EAAE,UAAU,IAAA,IAAQ,uBAAA;AAI1B,gBAAY,CAAC,cAAc;AACzB,UAAI,IAAI,OAAO;AACb,cAAM,cAAc,IAAI,MAAM,kBAAkB,CAAC,EAAE,WAAW;AAE5D,oBAAU,UAAU,UAAU,IAAI,EAAE,MAAM,CAAC,QAAQ;AACjD,oBAAQ,MAAM,qCAAqC,GAAG;AAAA,UACxD,CAAC;AAAA,QACH,CAAC;AAGD,kBAAU,WAAW;AAAA,MACvB;AAAA,IACF,CAAC;;;;;;ACVM,MAAM,yBAAyB,oBAAoBC,wBAA0B,EACjF,WAAWC,SAAe,EAC1B,MAAA;"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SelectionMenuRenderFn, SelectionMenuPropsBase } from '@embedpdf/utils/vue';
|
|
2
|
+
export interface SelectionSelectionContext {
|
|
3
|
+
type: 'selection';
|
|
4
|
+
pageIndex: number;
|
|
5
|
+
}
|
|
6
|
+
export type SelectionSelectionMenuRenderFn = SelectionMenuRenderFn<SelectionSelectionContext>;
|
|
7
|
+
export type SelectionSelectionMenuProps = SelectionMenuPropsBase<SelectionSelectionContext>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@embedpdf/plugin-selection",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-next.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -34,15 +34,17 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@embedpdf/models": "
|
|
37
|
+
"@embedpdf/models": "2.0.0-next.0",
|
|
38
|
+
"@embedpdf/utils": "2.0.0-next.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@types/react": "^18.2.0",
|
|
41
42
|
"typescript": "^5.0.0",
|
|
42
43
|
"@embedpdf/build": "1.1.0",
|
|
43
|
-
"@embedpdf/
|
|
44
|
-
"@embedpdf/
|
|
45
|
-
"@embedpdf/plugin-interaction-manager": "
|
|
44
|
+
"@embedpdf/plugin-viewport": "2.0.0-next.0",
|
|
45
|
+
"@embedpdf/core": "2.0.0-next.0",
|
|
46
|
+
"@embedpdf/plugin-interaction-manager": "2.0.0-next.0",
|
|
47
|
+
"@embedpdf/plugin-scroll": "2.0.0-next.0"
|
|
46
48
|
},
|
|
47
49
|
"peerDependencies": {
|
|
48
50
|
"react": ">=16.8.0",
|
|
@@ -50,9 +52,8 @@
|
|
|
50
52
|
"preact": "^10.26.4",
|
|
51
53
|
"vue": ">=3.2.0",
|
|
52
54
|
"svelte": ">=5 <6",
|
|
53
|
-
"@embedpdf/core": "
|
|
54
|
-
"@embedpdf/plugin-
|
|
55
|
-
"@embedpdf/plugin-interaction-manager": "1.5.0"
|
|
55
|
+
"@embedpdf/core": "2.0.0-next.0",
|
|
56
|
+
"@embedpdf/plugin-interaction-manager": "2.0.0-next.0"
|
|
56
57
|
},
|
|
57
58
|
"files": [
|
|
58
59
|
"dist",
|