@embedpdf/plugin-selection 2.4.0 → 2.5.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.
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),i=require("preact/jsx-runtime");require("preact");const o=require("preact/hooks"),n=require("@embedpdf/models"),r=require("@embedpdf/core/preact"),l=require("@embedpdf/utils/preact"),s=()=>r.useCapability(t.SelectionPlugin.id),u=()=>r.usePlugin(t.SelectionPlugin.id);function a(){const{provides:e}=s();return o.useEffect(()=>{if(e)return e.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e)})},[e]),null}const c=e.createPluginPackage(t.SelectionPluginPackage).addUtility(a).build();exports.CopyToClipboard=a,exports.MarqueeSelection=({documentId:e,pageIndex:t,scale:n,className:l,stroke:s="rgba(0,122,204,0.8)",fill:a="rgba(0,122,204,0.15)"})=>{const{plugin:c}=u(),d=r.useDocumentState(e),[g,p]=o.useState(null),x=o.useMemo(()=>void 0!==n?n:(null==d?void 0:d.scale)??1,[n,null==d?void 0:d.scale]);return o.useEffect(()=>{if(c&&e)return c.registerMarqueeOnPage({documentId:e,pageIndex:t,scale:x,onRectChange:p})},[c,e,t,x]),g?i.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:g.origin.x*x,top:g.origin.y*x,width:g.size.width*x,height:g.size.height*x,border:`1px dashed ${s}`,background:a,boxSizing:"border-box",zIndex:1e3},className:l}):null},exports.SelectionLayer=function({documentId:e,pageIndex:t,scale:s,rotation:a,background:c="rgba(33,150,243)",selectionMenu:d}){const{plugin:g}=u(),p=r.useDocumentState(e),[x,h]=o.useState([]),[b,f]=o.useState(null),[m,y]=o.useState(null);o.useEffect(()=>{if(g&&e)return g.registerSelectionOnPage({documentId:e,pageIndex:t,onRectsChange:({rects:e,boundingRect:t})=>{h(e),f(t)}})},[g,e,t]),o.useEffect(()=>{if(g&&e)return g.onMenuPlacement(e,e=>{y(e)})},[g,e]);const S=o.useMemo(()=>void 0!==s?s:(null==p?void 0:p.scale)??1,[s,null==p?void 0:p.scale]),v=o.useMemo(()=>void 0!==a?a:(null==p?void 0:p.rotation)??n.Rotation.Degree0,[a,null==p?void 0:p.rotation]),P=d&&m&&m.pageIndex===t&&m.isVisible;return b?i.jsxs(i.Fragment,{children:[i.jsx("div",{style:{position:"absolute",left:b.origin.x*S,top:b.origin.y*S,width:b.size.width*S,height:b.size.height*S,mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"},children:x.map((e,t)=>i.jsx("div",{style:{position:"absolute",left:(e.origin.x-b.origin.x)*S,top:(e.origin.y-b.origin.y)*S,width:e.size.width*S,height:e.size.height*S,background:c}},t))}),P&&i.jsx(l.CounterRotate,{rect:{origin:{x:m.rect.origin.x*S,y:m.rect.origin.y*S},size:{width:m.rect.size.width*S,height:m.rect.size.height*S}},rotation:v,children:e=>d({...e,context:{type:"selection",pageIndex:t},selected:!0,placement:m})})]}):null},exports.SelectionPluginPackage=c,exports.useSelectionCapability=s,exports.useSelectionPlugin=u,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),i=require("preact/jsx-runtime");require("preact");const o=require("preact/hooks"),n=require("@embedpdf/core/preact"),r=require("@embedpdf/utils/preact"),l=()=>n.useCapability(t.SelectionPlugin.id),s=()=>n.usePlugin(t.SelectionPlugin.id);function u(){const{provides:e}=l();return o.useEffect(()=>{if(e)return e.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e)})},[e]),null}const a=e.createPluginPackage(t.SelectionPluginPackage).addUtility(u).build();exports.CopyToClipboard=u,exports.MarqueeSelection=({documentId:e,pageIndex:t,scale:r,className:l,stroke:u="rgba(0,122,204,0.8)",fill:a="rgba(0,122,204,0.15)"})=>{const{plugin:c}=s(),d=n.useDocumentState(e),[g,p]=o.useState(null),x=o.useMemo(()=>void 0!==r?r:(null==d?void 0:d.scale)??1,[r,null==d?void 0:d.scale]);return o.useEffect(()=>{if(c&&e)return c.registerMarqueeOnPage({documentId:e,pageIndex:t,scale:x,onRectChange:p})},[c,e,t,x]),g?i.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:g.origin.x*x,top:g.origin.y*x,width:g.size.width*x,height:g.size.height*x,border:`1px dashed ${u}`,background:a,boxSizing:"border-box",zIndex:1e3},className:l}):null},exports.SelectionLayer=function({documentId:e,pageIndex:t,scale:l,rotation:u,background:a="rgba(33,150,243)",selectionMenu:c}){var d,g;const{plugin:p}=s(),x=n.useDocumentState(e),h=null==(g=null==(d=null==x?void 0:x.document)?void 0:d.pages)?void 0:g[t],[b,f]=o.useState([]),[m,v]=o.useState(null),[y,S]=o.useState(null);o.useEffect(()=>{if(p&&e)return p.registerSelectionOnPage({documentId:e,pageIndex:t,onRectsChange:({rects:e,boundingRect:t})=>{f(e),v(t)}})},[p,e,t]),o.useEffect(()=>{if(p&&e)return p.onMenuPlacement(e,e=>{S(e)})},[p,e]);const P=o.useMemo(()=>void 0!==l?l:(null==x?void 0:x.scale)??1,[l,null==x?void 0:x.scale]),z=o.useMemo(()=>{if(void 0!==u)return u;return(((null==h?void 0:h.rotation)??0)+((null==x?void 0:x.rotation)??0))%4},[u,null==h?void 0:h.rotation,null==x?void 0:x.rotation]),I=c&&y&&y.pageIndex===t&&y.isVisible;return m?i.jsxs(i.Fragment,{children:[i.jsx("div",{style:{position:"absolute",left:m.origin.x*P,top:m.origin.y*P,width:m.size.width*P,height:m.size.height*P,mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"},children:b.map((e,t)=>i.jsx("div",{style:{position:"absolute",left:(e.origin.x-m.origin.x)*P,top:(e.origin.y-m.origin.y)*P,width:e.size.width*P,height:e.size.height*P,background:a}},t))}),I&&i.jsx(r.CounterRotate,{rect:{origin:{x:y.rect.origin.x*P,y:y.rect.origin.y*P},size:{width:y.rect.size.width*P,height:y.rect.size.height*P}},rotation:z,children:e=>c({...e,context:{type:"selection",pageIndex:t},selected:!0,placement:y})})]}):null},exports.SelectionPluginPackage=a,exports.useSelectionCapability=l,exports.useSelectionPlugin=s,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts","../../src/shared/components/selection-layer.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n return documentState?.rotation ?? Rotation.Degree0;\n }, [rotationOverride, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","CopyToClipboard","provides","sel","useEffect","onCopyToClipboard","text","navigator","clipboard","writeText","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","build","documentId","pageIndex","scale","className","stroke","fill","plugin","selPlugin","documentState","useDocumentState","rect","setRect","useState","actualScale","useMemo","registerMarqueeOnPage","onRectChange","jsx","style","position","pointerEvents","left","origin","x","top","y","width","size","height","border","background","boxSizing","zIndex","scaleOverride","rotation","rotationOverride","selectionMenu","rects","setRects","boundingRect","setBoundingRect","placement","setPlacement","registerSelectionOnPage","onRectsChange","onMenuPlacement","newPlacement","actualRotation","Rotation","Degree0","shouldRenderMenu","isVisible","jsxs","Fragment","children","mixBlendMode","isolation","map","b","i","CounterRotate","props","context","type","selected"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts","../../src/shared/components/selection-layer.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const page = documentState?.document?.pages?.[pageIndex];\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n }, [rotationOverride, page?.rotation, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","CopyToClipboard","provides","sel","useEffect","onCopyToClipboard","text","navigator","clipboard","writeText","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","build","documentId","pageIndex","scale","className","stroke","fill","plugin","selPlugin","documentState","useDocumentState","rect","setRect","useState","actualScale","useMemo","registerMarqueeOnPage","onRectChange","jsx","style","position","pointerEvents","left","origin","x","top","y","width","size","height","border","background","boxSizing","zIndex","scaleOverride","rotation","rotationOverride","selectionMenu","page","_b","_a","document","pages","rects","setRects","boundingRect","setBoundingRect","placement","setPlacement","registerSelectionOnPage","onRectsChange","onMenuPlacement","newPlacement","actualRotation","shouldRenderMenu","isVisible","jsxs","Fragment","children","mixBlendMode","isolation","map","b","i","CounterRotate","props","context","type","selected"],"mappings":"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"}
@@ -4,7 +4,6 @@ export * from "@embedpdf/plugin-selection";
4
4
  import { jsxs, Fragment, jsx } from "preact/jsx-runtime";
5
5
  import "preact";
6
6
  import { useState, useEffect, useMemo } from "preact/hooks";
7
- import { Rotation } from "@embedpdf/models";
8
7
  import { useCapability, usePlugin, useDocumentState } from "@embedpdf/core/preact";
9
8
  import { CounterRotate } from "@embedpdf/utils/preact";
10
9
  const useSelectionCapability = () => useCapability(SelectionPlugin.id);
@@ -17,8 +16,10 @@ function SelectionLayer({
17
16
  background = "rgba(33,150,243)",
18
17
  selectionMenu
19
18
  }) {
19
+ var _a, _b;
20
20
  const { plugin: selPlugin } = useSelectionPlugin();
21
21
  const documentState = useDocumentState(documentId);
22
+ const page = (_b = (_a = documentState == null ? void 0 : documentState.document) == null ? void 0 : _a.pages) == null ? void 0 : _b[pageIndex];
22
23
  const [rects, setRects] = useState([]);
23
24
  const [boundingRect, setBoundingRect] = useState(null);
24
25
  const [placement, setPlacement] = useState(null);
@@ -45,8 +46,10 @@ function SelectionLayer({
45
46
  }, [scaleOverride, documentState == null ? void 0 : documentState.scale]);
46
47
  const actualRotation = useMemo(() => {
47
48
  if (rotationOverride !== void 0) return rotationOverride;
48
- return (documentState == null ? void 0 : documentState.rotation) ?? Rotation.Degree0;
49
- }, [rotationOverride, documentState == null ? void 0 : documentState.rotation]);
49
+ const pageRotation = (page == null ? void 0 : page.rotation) ?? 0;
50
+ const docRotation = (documentState == null ? void 0 : documentState.rotation) ?? 0;
51
+ return (pageRotation + docRotation) % 4;
52
+ }, [rotationOverride, page == null ? void 0 : page.rotation, documentState == null ? void 0 : documentState.rotation]);
50
53
  const shouldRenderMenu = selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;
51
54
  if (!boundingRect) return null;
52
55
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/selection-layer.tsx","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n return documentState?.rotation ?? Rotation.Degree0;\n }, [rotationOverride, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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":["rects","boundingRect","BaseSelectionPluginPackage"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/selection-layer.tsx","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const page = documentState?.document?.pages?.[pageIndex];\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n }, [rotationOverride, page?.rotation, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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":["rects","boundingRect","BaseSelectionPluginPackage"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),i=require("react/jsx-runtime"),o=require("react"),n=require("@embedpdf/models"),r=require("@embedpdf/core/react"),l=require("@embedpdf/utils/react"),s=()=>r.useCapability(t.SelectionPlugin.id),u=()=>r.usePlugin(t.SelectionPlugin.id);function a(){const{provides:e}=s();return o.useEffect(()=>{if(e)return e.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e)})},[e]),null}const c=e.createPluginPackage(t.SelectionPluginPackage).addUtility(a).build();exports.CopyToClipboard=a,exports.MarqueeSelection=({documentId:e,pageIndex:t,scale:n,className:l,stroke:s="rgba(0,122,204,0.8)",fill:a="rgba(0,122,204,0.15)"})=>{const{plugin:c}=u(),d=r.useDocumentState(e),[g,p]=o.useState(null),x=o.useMemo(()=>void 0!==n?n:(null==d?void 0:d.scale)??1,[n,null==d?void 0:d.scale]);return o.useEffect(()=>{if(c&&e)return c.registerMarqueeOnPage({documentId:e,pageIndex:t,scale:x,onRectChange:p})},[c,e,t,x]),g?i.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:g.origin.x*x,top:g.origin.y*x,width:g.size.width*x,height:g.size.height*x,border:`1px dashed ${s}`,background:a,boxSizing:"border-box",zIndex:1e3},className:l}):null},exports.SelectionLayer=function({documentId:e,pageIndex:t,scale:s,rotation:a,background:c="rgba(33,150,243)",selectionMenu:d}){const{plugin:g}=u(),p=r.useDocumentState(e),[x,b]=o.useState([]),[h,f]=o.useState(null),[m,y]=o.useState(null);o.useEffect(()=>{if(g&&e)return g.registerSelectionOnPage({documentId:e,pageIndex:t,onRectsChange:({rects:e,boundingRect:t})=>{b(e),f(t)}})},[g,e,t]),o.useEffect(()=>{if(g&&e)return g.onMenuPlacement(e,e=>{y(e)})},[g,e]);const S=o.useMemo(()=>void 0!==s?s:(null==p?void 0:p.scale)??1,[s,null==p?void 0:p.scale]),v=o.useMemo(()=>void 0!==a?a:(null==p?void 0:p.rotation)??n.Rotation.Degree0,[a,null==p?void 0:p.rotation]),P=d&&m&&m.pageIndex===t&&m.isVisible;return h?i.jsxs(i.Fragment,{children:[i.jsx("div",{style:{position:"absolute",left:h.origin.x*S,top:h.origin.y*S,width:h.size.width*S,height:h.size.height*S,mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"},children:x.map((e,t)=>i.jsx("div",{style:{position:"absolute",left:(e.origin.x-h.origin.x)*S,top:(e.origin.y-h.origin.y)*S,width:e.size.width*S,height:e.size.height*S,background:c}},t))}),P&&i.jsx(l.CounterRotate,{rect:{origin:{x:m.rect.origin.x*S,y:m.rect.origin.y*S},size:{width:m.rect.size.width*S,height:m.rect.size.height*S}},rotation:v,children:e=>d({...e,context:{type:"selection",pageIndex:t},selected:!0,placement:m})})]}):null},exports.SelectionPluginPackage=c,exports.useSelectionCapability=s,exports.useSelectionPlugin=u,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),i=require("react/jsx-runtime"),n=require("react"),o=require("@embedpdf/core/react"),r=require("@embedpdf/utils/react"),l=()=>o.useCapability(t.SelectionPlugin.id),s=()=>o.usePlugin(t.SelectionPlugin.id);function u(){const{provides:e}=l();return n.useEffect(()=>{if(e)return e.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e)})},[e]),null}const a=e.createPluginPackage(t.SelectionPluginPackage).addUtility(u).build();exports.CopyToClipboard=u,exports.MarqueeSelection=({documentId:e,pageIndex:t,scale:r,className:l,stroke:u="rgba(0,122,204,0.8)",fill:a="rgba(0,122,204,0.15)"})=>{const{plugin:c}=s(),d=o.useDocumentState(e),[g,p]=n.useState(null),x=n.useMemo(()=>void 0!==r?r:(null==d?void 0:d.scale)??1,[r,null==d?void 0:d.scale]);return n.useEffect(()=>{if(c&&e)return c.registerMarqueeOnPage({documentId:e,pageIndex:t,scale:x,onRectChange:p})},[c,e,t,x]),g?i.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:g.origin.x*x,top:g.origin.y*x,width:g.size.width*x,height:g.size.height*x,border:`1px dashed ${u}`,background:a,boxSizing:"border-box",zIndex:1e3},className:l}):null},exports.SelectionLayer=function({documentId:e,pageIndex:t,scale:l,rotation:u,background:a="rgba(33,150,243)",selectionMenu:c}){var d,g;const{plugin:p}=s(),x=o.useDocumentState(e),h=null==(g=null==(d=null==x?void 0:x.document)?void 0:d.pages)?void 0:g[t],[b,f]=n.useState([]),[m,v]=n.useState(null),[y,S]=n.useState(null);n.useEffect(()=>{if(p&&e)return p.registerSelectionOnPage({documentId:e,pageIndex:t,onRectsChange:({rects:e,boundingRect:t})=>{f(e),v(t)}})},[p,e,t]),n.useEffect(()=>{if(p&&e)return p.onMenuPlacement(e,e=>{S(e)})},[p,e]);const P=n.useMemo(()=>void 0!==l?l:(null==x?void 0:x.scale)??1,[l,null==x?void 0:x.scale]),z=n.useMemo(()=>{if(void 0!==u)return u;return(((null==h?void 0:h.rotation)??0)+((null==x?void 0:x.rotation)??0))%4},[u,null==h?void 0:h.rotation,null==x?void 0:x.rotation]),I=c&&y&&y.pageIndex===t&&y.isVisible;return m?i.jsxs(i.Fragment,{children:[i.jsx("div",{style:{position:"absolute",left:m.origin.x*P,top:m.origin.y*P,width:m.size.width*P,height:m.size.height*P,mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"},children:b.map((e,t)=>i.jsx("div",{style:{position:"absolute",left:(e.origin.x-m.origin.x)*P,top:(e.origin.y-m.origin.y)*P,width:e.size.width*P,height:e.size.height*P,background:a}},t))}),I&&i.jsx(r.CounterRotate,{rect:{origin:{x:y.rect.origin.x*P,y:y.rect.origin.y*P},size:{width:y.rect.size.width*P,height:y.rect.size.height*P}},rotation:z,children:e=>c({...e,context:{type:"selection",pageIndex:t},selected:!0,placement:y})})]}):null},exports.SelectionPluginPackage=a,exports.useSelectionCapability=l,exports.useSelectionPlugin=s,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts","../../src/shared/components/selection-layer.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n return documentState?.rotation ?? Rotation.Degree0;\n }, [rotationOverride, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","CopyToClipboard","provides","sel","useEffect","onCopyToClipboard","text","navigator","clipboard","writeText","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","build","documentId","pageIndex","scale","className","stroke","fill","plugin","selPlugin","documentState","useDocumentState","rect","setRect","useState","actualScale","useMemo","registerMarqueeOnPage","onRectChange","jsx","style","position","pointerEvents","left","origin","x","top","y","width","size","height","border","background","boxSizing","zIndex","scaleOverride","rotation","rotationOverride","selectionMenu","rects","setRects","boundingRect","setBoundingRect","placement","setPlacement","registerSelectionOnPage","onRectsChange","onMenuPlacement","newPlacement","actualRotation","Rotation","Degree0","shouldRenderMenu","isVisible","jsxs","Fragment","children","mixBlendMode","isolation","map","b","i","CounterRotate","props","context","type","selected"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts","../../src/shared/components/selection-layer.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const page = documentState?.document?.pages?.[pageIndex];\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n }, [rotationOverride, page?.rotation, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","CopyToClipboard","provides","sel","useEffect","onCopyToClipboard","text","navigator","clipboard","writeText","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","build","documentId","pageIndex","scale","className","stroke","fill","plugin","selPlugin","documentState","useDocumentState","rect","setRect","useState","actualScale","useMemo","registerMarqueeOnPage","onRectChange","jsx","style","position","pointerEvents","left","origin","x","top","y","width","size","height","border","background","boxSizing","zIndex","scaleOverride","rotation","rotationOverride","selectionMenu","page","_b","_a","document","pages","rects","setRects","boundingRect","setBoundingRect","placement","setPlacement","registerSelectionOnPage","onRectsChange","onMenuPlacement","newPlacement","actualRotation","shouldRenderMenu","isVisible","jsxs","Fragment","children","mixBlendMode","isolation","map","b","i","CounterRotate","props","context","type","selected"],"mappings":"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"}
@@ -3,7 +3,6 @@ import { SelectionPlugin, SelectionPluginPackage as SelectionPluginPackage$1 } f
3
3
  export * from "@embedpdf/plugin-selection";
4
4
  import { jsxs, Fragment, jsx } from "react/jsx-runtime";
5
5
  import { useState, useEffect, useMemo } from "react";
6
- import { Rotation } from "@embedpdf/models";
7
6
  import { useCapability, usePlugin, useDocumentState } from "@embedpdf/core/react";
8
7
  import { CounterRotate } from "@embedpdf/utils/react";
9
8
  const useSelectionCapability = () => useCapability(SelectionPlugin.id);
@@ -16,8 +15,10 @@ function SelectionLayer({
16
15
  background = "rgba(33,150,243)",
17
16
  selectionMenu
18
17
  }) {
18
+ var _a, _b;
19
19
  const { plugin: selPlugin } = useSelectionPlugin();
20
20
  const documentState = useDocumentState(documentId);
21
+ const page = (_b = (_a = documentState == null ? void 0 : documentState.document) == null ? void 0 : _a.pages) == null ? void 0 : _b[pageIndex];
21
22
  const [rects, setRects] = useState([]);
22
23
  const [boundingRect, setBoundingRect] = useState(null);
23
24
  const [placement, setPlacement] = useState(null);
@@ -44,8 +45,10 @@ function SelectionLayer({
44
45
  }, [scaleOverride, documentState == null ? void 0 : documentState.scale]);
45
46
  const actualRotation = useMemo(() => {
46
47
  if (rotationOverride !== void 0) return rotationOverride;
47
- return (documentState == null ? void 0 : documentState.rotation) ?? Rotation.Degree0;
48
- }, [rotationOverride, documentState == null ? void 0 : documentState.rotation]);
48
+ const pageRotation = (page == null ? void 0 : page.rotation) ?? 0;
49
+ const docRotation = (documentState == null ? void 0 : documentState.rotation) ?? 0;
50
+ return (pageRotation + docRotation) % 4;
51
+ }, [rotationOverride, page == null ? void 0 : page.rotation, documentState == null ? void 0 : documentState.rotation]);
49
52
  const shouldRenderMenu = selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;
50
53
  if (!boundingRect) return null;
51
54
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/selection-layer.tsx","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n return documentState?.rotation ?? Rotation.Degree0;\n }, [rotationOverride, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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":["rects","boundingRect","BaseSelectionPluginPackage"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-selection.ts","../../src/shared/components/selection-layer.tsx","../../src/shared/components/copy-to-clipboard.tsx","../../src/shared/components/marquee-selection.tsx","../../src/shared/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect, Rotation } from '@embedpdf/models';\nimport { useSelectionPlugin } from '../hooks';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { SelectionSelectionMenuRenderFn } from '../types';\nimport { CounterRotate } from '@embedpdf/utils/@framework';\n\ntype Props = {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n selectionMenu?: SelectionSelectionMenuRenderFn;\n};\n\nexport function SelectionLayer({\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n}: Props) {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const page = documentState?.document?.pages?.[pageIndex];\n const [rects, setRects] = useState<Rect[]>([]);\n const [boundingRect, setBoundingRect] = useState<Rect | null>(null);\n\n // Store the placement object from the plugin\n const [placement, setPlacement] = useState<SelectionMenuPlacement | null>(null);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects, boundingRect }) => {\n setRects(rects);\n setBoundingRect(boundingRect);\n },\n });\n }, [selPlugin, documentId, pageIndex]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n // Subscribe to menu placement changes for this specific document\n return selPlugin.onMenuPlacement(documentId, (newPlacement) => {\n // Optimization: We could filter here, but React state updates are cheap enough usually.\n // Ideally, check: if (newPlacement?.pageIndex === pageIndex)\n setPlacement(newPlacement);\n });\n }, [selPlugin, documentId]);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n const actualRotation = useMemo(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n }, [rotationOverride, page?.rotation, documentState?.rotation]);\n\n const shouldRenderMenu =\n selectionMenu && placement && placement.pageIndex === pageIndex && placement.isVisible;\n\n if (!boundingRect) return null;\n\n return (\n <>\n <div\n style={{\n position: 'absolute',\n left: boundingRect.origin.x * actualScale,\n top: boundingRect.origin.y * actualScale,\n width: boundingRect.size.width * actualScale,\n height: boundingRect.size.height * actualScale,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }}\n >\n {rects.map((b, i) => (\n <div\n key={i}\n style={{\n position: 'absolute',\n left: (b.origin.x - boundingRect.origin.x) * actualScale,\n top: (b.origin.y - boundingRect.origin.y) * actualScale,\n width: b.size.width * actualScale,\n height: b.size.height * actualScale,\n background,\n }}\n />\n ))}\n </div>\n {shouldRenderMenu && (\n <CounterRotate\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 {(props) =>\n selectionMenu({\n ...props,\n context: {\n type: 'selection',\n pageIndex,\n },\n selected: true,\n placement,\n })\n }\n </CounterRotate>\n )}\n </>\n );\n}\n","import { useEffect } from '@framework';\n\nimport { useSelectionCapability } from '../hooks';\n\nexport function CopyToClipboard() {\n const { provides: sel } = useSelectionCapability();\n\n useEffect(() => {\n if (!sel) return;\n return sel.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text);\n });\n }, [sel]);\n\n return null;\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { useDocumentState } from '@embedpdf/core/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\n/**\n * MarqueeSelection renders a selection rectangle when the user drags to select items.\n * Place this component on each page where you want marquee selection to work.\n *\n * Other plugins (e.g., annotation, form) can subscribe to `onMarqueeEnd` to\n * determine which objects intersect with the marquee rect.\n */\nexport const MarqueeSelection = ({\n documentId,\n pageIndex,\n scale,\n className,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n}: MarqueeSelectionProps) => {\n const { plugin: selPlugin } = useSelectionPlugin();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scale !== undefined) return scale;\n return documentState?.scale ?? 1;\n }, [scale, documentState?.scale]);\n\n useEffect(() => {\n if (!selPlugin || !documentId) return;\n\n return selPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: setRect,\n });\n }, [selPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }}\n className={className}\n />\n );\n};\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":["rects","boundingRect","BaseSelectionPluginPackage"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection");require("svelte/internal/disclose-version");const i=require("svelte/internal/client"),n=require("@embedpdf/models"),o=require("@embedpdf/core/svelte"),r=require("@embedpdf/utils/svelte");function l(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const i in e)if("default"!==i){const n=Object.getOwnPropertyDescriptor(e,i);Object.defineProperty(t,i,n.get?n:{enumerable:!0,get:()=>e[i]})}return t.default=e,Object.freeze(t)}const s=l(i),p=()=>o.useCapability(t.SelectionPlugin.id),c=()=>o.usePlugin(t.SelectionPlugin.id);var d=s.from_html("<div></div>"),g=s.from_html("<div></div> <!>",1);function a(e,t){s.push(t,!0);const i=p();s.user_effect(()=>{if(i.provides)return i.provides.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e).catch(e=>{console.error("Failed to copy text to clipboard:",e)})})}),s.pop()}var u=s.from_html("<div></div>");const f=e.createPluginPackage(t.SelectionPluginPackage).addUtility(a).build();exports.CopyToClipboard=a,exports.MarqueeSelection=function(e,t){s.push(t,!0);let i=s.prop(t,"stroke",3,"rgba(0,122,204,0.8)"),n=s.prop(t,"fill",3,"rgba(0,122,204,0.15)");const r=c(),l=o.useDocumentState(()=>t.documentId);let p=s.state(null);const d=s.derived(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=l.current)?void 0:e.scale)??1});s.user_effect(()=>{if(s.set(p,null),r.plugin)return r.plugin.registerMarqueeOnPage({documentId:t.documentId,pageIndex:t.pageIndex,scale:s.get(d),onRectChange:e=>{s.set(p,e,!0)}})});var g=s.comment(),a=s.first_child(g),f=e=>{var o=u();let r;s.template_effect(()=>{s.set_class(o,1,s.clsx(t.class)),r=s.set_style(o,"",r,{position:"absolute","pointer-events":"none",left:s.get(p).origin.x*s.get(d)+"px",top:s.get(p).origin.y*s.get(d)+"px",width:s.get(p).size.width*s.get(d)+"px",height:s.get(p).size.height*s.get(d)+"px",border:`1px dashed ${i()}`,background:n(),"box-sizing":"border-box","z-index":"1000"})}),s.append(e,o)};s.if(a,e=>{s.get(p)&&e(f)}),s.append(e,g),s.pop()},exports.SelectionLayer=function(e,t){s.push(t,!0);let i=s.prop(t,"background",3,"rgba(33,150,243)");const l=c(),p=o.useDocumentState(()=>t.documentId);let a=s.state(s.proxy([])),u=s.state(null),f=s.state(null);const v=s.derived(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=p.current)?void 0:e.scale)??1}),m=s.derived(()=>{var e;return void 0!==t.rotation?t.rotation:(null==(e=p.current)?void 0:e.rotation)??n.Rotation.Degree0}),x=s.derived(()=>Boolean(s.get(f)&&s.get(f).pageIndex===t.pageIndex&&s.get(f).isVisible&&(t.selectionMenu||t.selectionMenuSnippet)));s.user_effect(()=>l.plugin&&t.documentId?l.plugin.registerSelectionOnPage({documentId:t.documentId,pageIndex:t.pageIndex,onRectsChange:({rects:e,boundingRect:t})=>{s.set(a,e,!0),s.set(u,t,!0)}}):(s.set(a,[],!0),void s.set(u,null))),s.user_effect(()=>{if(l.plugin&&t.documentId)return l.plugin.onMenuPlacement(t.documentId,e=>{s.set(f,e,!0)});s.set(f,null)});var h=s.comment(),b=s.first_child(h),y=e=>{var n=g(),o=s.first_child(n);let l;s.each(o,21,()=>s.get(a),s.index,(e,t)=>{var n=d();let o;s.template_effect(()=>o=s.set_style(n,"",o,{position:"absolute",left:(s.get(t).origin.x-s.get(u).origin.x)*s.get(v)+"px",top:(s.get(t).origin.y-s.get(u).origin.y)*s.get(v)+"px",width:s.get(t).size.width*s.get(v)+"px",height:s.get(t).size.height*s.get(v)+"px",background:i(),"pointer-events":"none"})),s.append(e,n)}),s.reset(o);var p=s.sibling(o,2),c=e=>{{const i=(e,i)=>{const n=s.derived(()=>function(e,i){return{context:{type:"selection",pageIndex:t.pageIndex},selected:!0,rect:e,placement:{suggestTop:(null==(n=s.get(f))?void 0:n.suggestTop)??!1,spaceAbove:(null==(o=s.get(f))?void 0:o.spaceAbove)??0,spaceBelow:(null==(r=s.get(f))?void 0:r.spaceBelow)??0},menuWrapperProps:i};var n,o,r}(null==i?void 0:i().rect,null==i?void 0:i().menuWrapperProps));var o=s.comment(),r=s.first_child(o),l=e=>{const i=s.derived(()=>t.selectionMenu(s.get(n)));var o=s.comment(),r=s.first_child(o),l=e=>{var t=s.comment(),n=s.first_child(t);s.component(n,()=>s.get(i).component,(e,t)=>{t(e,s.spread_props(()=>s.get(i).props))}),s.append(e,t)};s.if(r,e=>{s.get(i)&&e(l)}),s.append(e,o)},p=e=>{var i=s.comment(),o=s.first_child(i),r=e=>{var i=s.comment(),o=s.first_child(i);s.snippet(o,()=>t.selectionMenuSnippet,()=>s.get(n)),s.append(e,i)};s.if(o,e=>{t.selectionMenuSnippet&&e(r)},!0),s.append(e,i)};s.if(r,e=>{t.selectionMenu?e(l):e(p,!1)}),s.append(e,o)};let n=s.derived(()=>({origin:{x:s.get(f).rect.origin.x*s.get(v),y:s.get(f).rect.origin.y*s.get(v)},size:{width:s.get(f).rect.size.width*s.get(v),height:s.get(f).rect.size.height*s.get(v)}}));r.CounterRotate(e,{get rect(){return s.get(n)},get rotation(){return s.get(m)},children:i,$$slots:{default:!0}})}};s.if(p,e=>{s.get(x)&&s.get(f)&&e(c)}),s.template_effect(()=>l=s.set_style(o,"",l,{position:"absolute",left:s.get(u).origin.x*s.get(v)+"px",top:s.get(u).origin.y*s.get(v)+"px",width:s.get(u).size.width*s.get(v)+"px",height:s.get(u).size.height*s.get(v)+"px","mix-blend-mode":"multiply",isolation:"isolate","pointer-events":"none"})),s.append(e,n)};s.if(b,e=>{s.get(u)&&e(y)}),s.append(e,h),s.pop()},exports.SelectionPluginPackage=f,exports.useSelectionCapability=p,exports.useSelectionPlugin=c,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection");require("svelte/internal/disclose-version");const n=require("svelte/internal/client");require("@embedpdf/models");const i=require("@embedpdf/core/svelte"),o=require("@embedpdf/utils/svelte");function r(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const n in e)if("default"!==n){const i=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,i.get?i:{enumerable:!0,get:()=>e[n]})}return t.default=e,Object.freeze(t)}const l=r(n),s=()=>i.useCapability(t.SelectionPlugin.id),p=()=>i.usePlugin(t.SelectionPlugin.id);var d=l.from_html("<div></div>"),c=l.from_html("<div></div> <!>",1);function g(e,t){l.push(t,!0);const n=s();l.user_effect(()=>{if(n.provides)return n.provides.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e).catch(e=>{console.error("Failed to copy text to clipboard:",e)})})}),l.pop()}var a=l.from_html("<div></div>");const u=e.createPluginPackage(t.SelectionPluginPackage).addUtility(g).build();exports.CopyToClipboard=g,exports.MarqueeSelection=function(e,t){l.push(t,!0);let n=l.prop(t,"stroke",3,"rgba(0,122,204,0.8)"),o=l.prop(t,"fill",3,"rgba(0,122,204,0.15)");const r=p(),s=i.useDocumentState(()=>t.documentId);let d=l.state(null);const c=l.derived(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=s.current)?void 0:e.scale)??1});l.user_effect(()=>{if(l.set(d,null),r.plugin)return r.plugin.registerMarqueeOnPage({documentId:t.documentId,pageIndex:t.pageIndex,scale:l.get(c),onRectChange:e=>{l.set(d,e,!0)}})});var g=l.comment(),u=l.first_child(g),v=e=>{var i=a();let r;l.template_effect(()=>{l.set_class(i,1,l.clsx(t.class)),r=l.set_style(i,"",r,{position:"absolute","pointer-events":"none",left:l.get(d).origin.x*l.get(c)+"px",top:l.get(d).origin.y*l.get(c)+"px",width:l.get(d).size.width*l.get(c)+"px",height:l.get(d).size.height*l.get(c)+"px",border:`1px dashed ${n()}`,background:o(),"box-sizing":"border-box","z-index":"1000"})}),l.append(e,i)};l.if(u,e=>{l.get(d)&&e(v)}),l.append(e,g),l.pop()},exports.SelectionLayer=function(e,t){l.push(t,!0);let n=l.prop(t,"background",3,"rgba(33,150,243)");const r=p(),s=i.useDocumentState(()=>t.documentId),g=l.derived(()=>{var e,n,i;return null==(i=null==(n=null==(e=s.current)?void 0:e.document)?void 0:n.pages)?void 0:i[t.pageIndex]});let a=l.state(l.proxy([])),u=l.state(null),v=l.state(null);const f=l.derived(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=s.current)?void 0:e.scale)??1}),m=l.derived(()=>{var e,n;if(void 0!==t.rotation)return t.rotation;return(((null==(e=l.get(g))?void 0:e.rotation)??0)+((null==(n=s.current)?void 0:n.rotation)??0))%4}),x=l.derived(()=>Boolean(l.get(v)&&l.get(v).pageIndex===t.pageIndex&&l.get(v).isVisible&&(t.selectionMenu||t.selectionMenuSnippet)));l.user_effect(()=>r.plugin&&t.documentId?r.plugin.registerSelectionOnPage({documentId:t.documentId,pageIndex:t.pageIndex,onRectsChange:({rects:e,boundingRect:t})=>{l.set(a,e,!0),l.set(u,t,!0)}}):(l.set(a,[],!0),void l.set(u,null))),l.user_effect(()=>{if(r.plugin&&t.documentId)return r.plugin.onMenuPlacement(t.documentId,e=>{l.set(v,e,!0)});l.set(v,null)});var h=l.comment(),b=l.first_child(h),y=e=>{var i=c(),r=l.first_child(i);let s;l.each(r,21,()=>l.get(a),l.index,(e,t)=>{var i=d();let o;l.template_effect(()=>o=l.set_style(i,"",o,{position:"absolute",left:(l.get(t).origin.x-l.get(u).origin.x)*l.get(f)+"px",top:(l.get(t).origin.y-l.get(u).origin.y)*l.get(f)+"px",width:l.get(t).size.width*l.get(f)+"px",height:l.get(t).size.height*l.get(f)+"px",background:n(),"pointer-events":"none"})),l.append(e,i)}),l.reset(r);var p=l.sibling(r,2),g=e=>{{const n=(e,n)=>{const i=l.derived(()=>function(e,n){return{context:{type:"selection",pageIndex:t.pageIndex},selected:!0,rect:e,placement:{suggestTop:(null==(i=l.get(v))?void 0:i.suggestTop)??!1,spaceAbove:(null==(o=l.get(v))?void 0:o.spaceAbove)??0,spaceBelow:(null==(r=l.get(v))?void 0:r.spaceBelow)??0},menuWrapperProps:n};var i,o,r}(null==n?void 0:n().rect,null==n?void 0:n().menuWrapperProps));var o=l.comment(),r=l.first_child(o),s=e=>{const n=l.derived(()=>t.selectionMenu(l.get(i)));var o=l.comment(),r=l.first_child(o),s=e=>{var t=l.comment(),i=l.first_child(t);l.component(i,()=>l.get(n).component,(e,t)=>{t(e,l.spread_props(()=>l.get(n).props))}),l.append(e,t)};l.if(r,e=>{l.get(n)&&e(s)}),l.append(e,o)},p=e=>{var n=l.comment(),o=l.first_child(n),r=e=>{var n=l.comment(),o=l.first_child(n);l.snippet(o,()=>t.selectionMenuSnippet,()=>l.get(i)),l.append(e,n)};l.if(o,e=>{t.selectionMenuSnippet&&e(r)},!0),l.append(e,n)};l.if(r,e=>{t.selectionMenu?e(s):e(p,!1)}),l.append(e,o)};let i=l.derived(()=>({origin:{x:l.get(v).rect.origin.x*l.get(f),y:l.get(v).rect.origin.y*l.get(f)},size:{width:l.get(v).rect.size.width*l.get(f),height:l.get(v).rect.size.height*l.get(f)}}));o.CounterRotate(e,{get rect(){return l.get(i)},get rotation(){return l.get(m)},children:n,$$slots:{default:!0}})}};l.if(p,e=>{l.get(x)&&l.get(v)&&e(g)}),l.template_effect(()=>s=l.set_style(r,"",s,{position:"absolute",left:l.get(u).origin.x*l.get(f)+"px",top:l.get(u).origin.y*l.get(f)+"px",width:l.get(u).size.width*l.get(f)+"px",height:l.get(u).size.height*l.get(f)+"px","mix-blend-mode":"multiply",isolation:"isolate","pointer-events":"none"})),l.append(e,i)};l.if(b,e=>{l.get(u)&&e(y)}),l.append(e,h),l.pop()},exports.SelectionPluginPackage=u,exports.useSelectionCapability=s,exports.useSelectionPlugin=p,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/index.ts","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/components/SelectionLayer.svelte"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\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 lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\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","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px dashed ${stroke}`}\n style:background={fill}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for selection rectangles */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived(\n rotationOverride !== undefined\n ? rotationOverride\n : (documentState.current?.rotation ?? Rotation.Degree0),\n );\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\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 {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","selectionCapability","$","user_effect","provides","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build","stroke","fill","selectionPlugin","documentState","useDocumentState","$$props","documentId","rect","state","actualScale","derived","scale","_a","current","set","plugin","registerMarqueeOnPage","pageIndex","onRectChange","newRect","div","root_1","clsx","class","styles","left","get","origin","x","top","y","width","size","height","consequent","background","rects","proxy","boundingRect","placement","actualRotation","rotation","Rotation","Degree0","shouldRenderMenu","Boolean","isVisible","selectionMenu","selectionMenuSnippet","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","onMenuPlacement","newPlacement","first_child","fragment_1","index","$$anchor","div_1","root_2","styles_1","children","$$arg0","menuProps","menuWrapperProps","context","type","selected","suggestTop","spaceAbove","spaceBelow","buildMenuProps","result","result_component","spread_props","props","CounterRotate","$$render","consequent_3","consequent_4"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/index.ts","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/components/SelectionLayer.svelte"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\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 lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\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","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px dashed ${stroke}`}\n style:background={fill}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for selection rectangles */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n const page = $derived(documentState.current?.document?.pages?.[pageIndex]);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived.by(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState.current?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n });\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\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 {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","selectionCapability","$","user_effect","provides","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build","stroke","fill","selectionPlugin","documentState","useDocumentState","$$props","documentId","rect","state","actualScale","derived","scale","_a","current","set","plugin","registerMarqueeOnPage","pageIndex","onRectChange","newRect","div","root_1","clsx","class","styles","left","get","origin","x","top","y","width","size","height","consequent","background","page","_c","_b","document","pages","rects","proxy","boundingRect","placement","actualRotation","rotation","shouldRenderMenu","Boolean","isVisible","selectionMenu","selectionMenuSnippet","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","onMenuPlacement","newPlacement","first_child","fragment_1","index","$$anchor","div_1","root_2","styles_1","children","$$arg0","menuProps","menuWrapperProps","context","type","selected","suggestTop","spaceAbove","spaceBelow","buildMenuProps","result","result_component","spread_props","props","CounterRotate","$$render","consequent_3","consequent_4"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { SelectionPlugin, SelectionPluginPackage as SelectionPluginPackage$1 } f
3
3
  export * from "@embedpdf/plugin-selection";
4
4
  import "svelte/internal/disclose-version";
5
5
  import * as $ from "svelte/internal/client";
6
- import { Rotation } from "@embedpdf/models";
6
+ import "@embedpdf/models";
7
7
  import { useCapability, usePlugin, useDocumentState } from "@embedpdf/core/svelte";
8
8
  import { CounterRotate } from "@embedpdf/utils/svelte";
9
9
  const useSelectionCapability = () => useCapability(SelectionPlugin.id);
@@ -15,6 +15,10 @@ function SelectionLayer($$anchor, $$props) {
15
15
  let background = $.prop($$props, "background", 3, "rgba(33,150,243)");
16
16
  const selectionPlugin = useSelectionPlugin();
17
17
  const documentState = useDocumentState(() => $$props.documentId);
18
+ const page = $.derived(() => {
19
+ var _a, _b, _c;
20
+ return (_c = (_b = (_a = documentState.current) == null ? void 0 : _a.document) == null ? void 0 : _b.pages) == null ? void 0 : _c[$$props.pageIndex];
21
+ });
18
22
  let rects = $.state($.proxy([]));
19
23
  let boundingRect = $.state(null);
20
24
  let placement = $.state(null);
@@ -23,8 +27,11 @@ function SelectionLayer($$anchor, $$props) {
23
27
  return $$props.scale !== void 0 ? $$props.scale : ((_a = documentState.current) == null ? void 0 : _a.scale) ?? 1;
24
28
  });
25
29
  const actualRotation = $.derived(() => {
26
- var _a;
27
- return $$props.rotation !== void 0 ? $$props.rotation : ((_a = documentState.current) == null ? void 0 : _a.rotation) ?? Rotation.Degree0;
30
+ var _a, _b;
31
+ if ($$props.rotation !== void 0) return $$props.rotation;
32
+ const pageRotation = ((_a = $.get(page)) == null ? void 0 : _a.rotation) ?? 0;
33
+ const docRotation = ((_b = documentState.current) == null ? void 0 : _b.rotation) ?? 0;
34
+ return (pageRotation + docRotation) % 4;
28
35
  });
29
36
  const shouldRenderMenu = $.derived(() => Boolean($.get(placement) && $.get(placement).pageIndex === $$props.pageIndex && $.get(placement).isVisible && ($$props.selectionMenu || $$props.selectionMenuSnippet)));
30
37
  $.user_effect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/SelectionLayer.svelte","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\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 lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for selection rectangles */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived(\n rotationOverride !== undefined\n ? rotationOverride\n : (documentState.current?.rotation ?? Rotation.Degree0),\n );\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\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 {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n","<script lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\n","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px dashed ${stroke}`}\n style:background={fill}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\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":["$$anchor","BaseSelectionPluginPackage"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/SelectionLayer.svelte","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\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 lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for selection rectangles */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n const page = $derived(documentState.current?.document?.pages?.[pageIndex]);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived.by(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState.current?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n });\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\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 {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n","<script lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\n","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px dashed ${stroke}`}\n style:background={fill}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\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":["$$anchor","BaseSelectionPluginPackage"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),l=require("vue"),o=require("@embedpdf/core/vue"),n=require("@embedpdf/models"),a=require("@embedpdf/utils/vue"),i=()=>o.useCapability(t.SelectionPlugin.id),u=()=>o.usePlugin(t.SelectionPlugin.id),r=l.defineComponent({__name:"selection-layer",props:{documentId:{},pageIndex:{},scale:{},rotation:{default:n.Rotation.Degree0},background:{default:"rgba(33,150,243)"},selectionMenu:{}},setup(e){const t=e,i=l.useSlots(),{plugin:r}=u(),c=o.useDocumentState(()=>t.documentId),d=l.ref([]),s=l.ref(null),p=l.ref(null),v=l.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=c.value)?void 0:e.scale)??1}),g=l.computed(()=>{var e;return void 0!==t.rotation?t.rotation:(null==(e=c.value)?void 0:e.rotation)??n.Rotation.Degree0}),m=l.computed(()=>!!p.value&&(p.value.pageIndex===t.pageIndex&&(!!p.value.isVisible&&(!!t.selectionMenu||!!i["selection-menu"]))));l.watch([()=>r.value,()=>t.documentId,()=>t.pageIndex],([e,t,l],o,n)=>{if(!e||!t)return d.value=[],void(s.value=null);n(e.registerSelectionOnPage({documentId:t,pageIndex:l,onRectsChange:({rects:e,boundingRect:t})=>{d.value=e,s.value=t}}))},{immediate:!0}),l.watch([()=>r.value,()=>t.documentId],([e,t],l,o)=>{if(!e||!t)return void(p.value=null);o(e.onMenuPlacement(t,e=>{p.value=e}))},{immediate:!0});const x=()=>({type:"selection",pageIndex:t.pageIndex}),b=()=>{var e,t,l;return{suggestTop:(null==(e=p.value)?void 0:e.suggestTop)??!1,spaceAbove:(null==(t=p.value)?void 0:t.spaceAbove)??0,spaceBelow:(null==(l=p.value)?void 0:l.spaceBelow)??0}},y=(e,l)=>t.selectionMenu?t.selectionMenu({rect:e,menuWrapperProps:l,selected:!0,placement:b(),context:x()}):null;return(t,o)=>s.value?(l.openBlock(),l.createElementBlock(l.Fragment,{key:0},[l.createElementVNode("div",{style:l.normalizeStyle({position:"absolute",left:s.value.origin.x*v.value+"px",top:s.value.origin.y*v.value+"px",width:s.value.size.width*v.value+"px",height:s.value.size.height*v.value+"px",mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"})},[(l.openBlock(!0),l.createElementBlock(l.Fragment,null,l.renderList(d.value,(t,o)=>(l.openBlock(),l.createElementBlock("div",{key:o,style:l.normalizeStyle({position:"absolute",left:(t.origin.x-s.value.origin.x)*v.value+"px",top:(t.origin.y-s.value.origin.y)*v.value+"px",width:t.size.width*v.value+"px",height:t.size.height*v.value+"px",background:e.background})},null,4))),128))],4),m.value?(l.openBlock(),l.createBlock(l.unref(a.CounterRotate),{key:0,rect:{origin:{x:p.value.rect.origin.x*v.value,y:p.value.rect.origin.y*v.value},size:{width:p.value.rect.size.width*v.value,height:p.value.rect.size.height*v.value}},rotation:g.value},{default:l.withCtx(({rect:o,menuWrapperProps:n})=>[e.selectionMenu?(l.openBlock(),l.createBlock(l.resolveDynamicComponent(y(o,n)),{key:0})):l.renderSlot(t.$slots,"selection-menu",{key:1,context:x(),selected:!0,rect:o,placement:b(),menuWrapperProps:n})]),_:3},8,["rect","rotation"])):l.createCommentVNode("",!0)],64)):l.createCommentVNode("",!0)}}),c=l.defineComponent({__name:"copy-to-clipboard",setup(e){const{provides:t}=i();return l.watchEffect(e=>{if(t.value){e(t.value.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e).catch(e=>{console.error("Failed to copy text to clipboard:",e)})}))}}),(e,t)=>null}}),d=l.defineComponent({__name:"marquee-selection",props:{documentId:{},pageIndex:{},scale:{},className:{},stroke:{default:"rgba(0,122,204,0.8)"},fill:{default:"rgba(0,122,204,0.15)"}},setup(e){const t=e,{plugin:n}=u(),a=o.useDocumentState(()=>t.documentId),i=l.ref(null),r=l.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=a.value)?void 0:e.scale)??1});return l.watch([n,()=>t.documentId,()=>t.pageIndex,r],([e,t,l,o],n,a)=>{if(i.value=null,!e)return;const u=e.registerMarqueeOnPage({documentId:t,pageIndex:l,scale:o,onRectChange:e=>{i.value=e}});a(()=>{null==u||u()})},{immediate:!0}),(t,o)=>i.value?(l.openBlock(),l.createElementBlock("div",{key:0,style:l.normalizeStyle({position:"absolute",pointerEvents:"none",left:i.value.origin.x*r.value+"px",top:i.value.origin.y*r.value+"px",width:i.value.size.width*r.value+"px",height:i.value.size.height*r.value+"px",border:`1px dashed ${e.stroke}`,background:e.fill,boxSizing:"border-box",zIndex:1e3}),class:l.normalizeClass(e.className)},null,6)):l.createCommentVNode("",!0)}}),s=e.createPluginPackage(t.SelectionPluginPackage).addUtility(c).build();exports.CopyToClipboard=c,exports.MarqueeSelection=d,exports.SelectionLayer=r,exports.SelectionPluginPackage=s,exports.useSelectionCapability=i,exports.useSelectionPlugin=u,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),l=require("vue"),o=require("@embedpdf/core/vue"),n=require("@embedpdf/models"),a=require("@embedpdf/utils/vue"),u=()=>o.useCapability(t.SelectionPlugin.id),i=()=>o.usePlugin(t.SelectionPlugin.id),r=l.defineComponent({__name:"selection-layer",props:{documentId:{},pageIndex:{},scale:{},rotation:{default:n.Rotation.Degree0},background:{default:"rgba(33,150,243)"},selectionMenu:{}},setup(e){const t=e,n=l.useSlots(),{plugin:u}=i(),r=o.useDocumentState(()=>t.documentId),c=l.computed(()=>{var e,l,o;return null==(o=null==(l=null==(e=r.value)?void 0:e.document)?void 0:l.pages)?void 0:o[t.pageIndex]}),d=l.ref([]),s=l.ref(null),p=l.ref(null),v=l.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=r.value)?void 0:e.scale)??1}),g=l.computed(()=>{var e,l;if(void 0!==t.rotation)return t.rotation;return(((null==(e=c.value)?void 0:e.rotation)??0)+((null==(l=r.value)?void 0:l.rotation)??0))%4}),m=l.computed(()=>!!p.value&&(p.value.pageIndex===t.pageIndex&&(!!p.value.isVisible&&(!!t.selectionMenu||!!n["selection-menu"]))));l.watch([()=>u.value,()=>t.documentId,()=>t.pageIndex],([e,t,l],o,n)=>{if(!e||!t)return d.value=[],void(s.value=null);n(e.registerSelectionOnPage({documentId:t,pageIndex:l,onRectsChange:({rects:e,boundingRect:t})=>{d.value=e,s.value=t}}))},{immediate:!0}),l.watch([()=>u.value,()=>t.documentId],([e,t],l,o)=>{if(!e||!t)return void(p.value=null);o(e.onMenuPlacement(t,e=>{p.value=e}))},{immediate:!0});const x=()=>({type:"selection",pageIndex:t.pageIndex}),b=()=>{var e,t,l;return{suggestTop:(null==(e=p.value)?void 0:e.suggestTop)??!1,spaceAbove:(null==(t=p.value)?void 0:t.spaceAbove)??0,spaceBelow:(null==(l=p.value)?void 0:l.spaceBelow)??0}},y=(e,l)=>t.selectionMenu?t.selectionMenu({rect:e,menuWrapperProps:l,selected:!0,placement:b(),context:x()}):null;return(t,o)=>s.value?(l.openBlock(),l.createElementBlock(l.Fragment,{key:0},[l.createElementVNode("div",{style:l.normalizeStyle({position:"absolute",left:s.value.origin.x*v.value+"px",top:s.value.origin.y*v.value+"px",width:s.value.size.width*v.value+"px",height:s.value.size.height*v.value+"px",mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"})},[(l.openBlock(!0),l.createElementBlock(l.Fragment,null,l.renderList(d.value,(t,o)=>(l.openBlock(),l.createElementBlock("div",{key:o,style:l.normalizeStyle({position:"absolute",left:(t.origin.x-s.value.origin.x)*v.value+"px",top:(t.origin.y-s.value.origin.y)*v.value+"px",width:t.size.width*v.value+"px",height:t.size.height*v.value+"px",background:e.background})},null,4))),128))],4),m.value?(l.openBlock(),l.createBlock(l.unref(a.CounterRotate),{key:0,rect:{origin:{x:p.value.rect.origin.x*v.value,y:p.value.rect.origin.y*v.value},size:{width:p.value.rect.size.width*v.value,height:p.value.rect.size.height*v.value}},rotation:g.value},{default:l.withCtx(({rect:o,menuWrapperProps:n})=>[e.selectionMenu?(l.openBlock(),l.createBlock(l.resolveDynamicComponent(y(o,n)),{key:0})):l.renderSlot(t.$slots,"selection-menu",{key:1,context:x(),selected:!0,rect:o,placement:b(),menuWrapperProps:n})]),_:3},8,["rect","rotation"])):l.createCommentVNode("",!0)],64)):l.createCommentVNode("",!0)}}),c=l.defineComponent({__name:"copy-to-clipboard",setup(e){const{provides:t}=u();return l.watchEffect(e=>{if(t.value){e(t.value.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e).catch(e=>{console.error("Failed to copy text to clipboard:",e)})}))}}),(e,t)=>null}}),d=l.defineComponent({__name:"marquee-selection",props:{documentId:{},pageIndex:{},scale:{},className:{},stroke:{default:"rgba(0,122,204,0.8)"},fill:{default:"rgba(0,122,204,0.15)"}},setup(e){const t=e,{plugin:n}=i(),a=o.useDocumentState(()=>t.documentId),u=l.ref(null),r=l.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=a.value)?void 0:e.scale)??1});return l.watch([n,()=>t.documentId,()=>t.pageIndex,r],([e,t,l,o],n,a)=>{if(u.value=null,!e)return;const i=e.registerMarqueeOnPage({documentId:t,pageIndex:l,scale:o,onRectChange:e=>{u.value=e}});a(()=>{null==i||i()})},{immediate:!0}),(t,o)=>u.value?(l.openBlock(),l.createElementBlock("div",{key:0,style:l.normalizeStyle({position:"absolute",pointerEvents:"none",left:u.value.origin.x*r.value+"px",top:u.value.origin.y*r.value+"px",width:u.value.size.width*r.value+"px",height:u.value.size.height*r.value+"px",border:`1px dashed ${e.stroke}`,background:e.fill,boxSizing:"border-box",zIndex:1e3}),class:l.normalizeClass(e.className)},null,6)):l.createCommentVNode("",!0)}}),s=e.createPluginPackage(t.SelectionPluginPackage).addUtility(c).build();exports.CopyToClipboard=c,exports.MarqueeSelection=d,exports.SelectionLayer=r,exports.SelectionPluginPackage=s,exports.useSelectionCapability=u,exports.useSelectionPlugin=i,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/vue/hooks/use-selection.ts","../../src/vue/components/selection-layer.vue","../../src/vue/components/copy-to-clipboard.vue","../../src/vue/components/marquee-selection.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","<template>\n <div\n v-if=\"rect\"\n :style=\"{\n position: 'absolute',\n pointerEvents: 'none',\n left: `${rect.origin.x * actualScale}px`,\n top: `${rect.origin.y * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }\"\n :class=\"className\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport type { Rect } from '@embedpdf/models';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\nconst props = withDefaults(defineProps<MarqueeSelectionProps>(), {\n stroke: 'rgba(0,122,204,0.8)',\n fill: 'rgba(0,122,204,0.15)',\n});\n\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst rect = ref<Rect | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nwatch(\n [selPlugin, () => props.documentId, () => props.pageIndex, actualScale],\n ([plugin, docId, pageIdx, scale], _, onCleanup) => {\n rect.value = null;\n\n if (!plugin) {\n return;\n }\n\n const unregister = plugin.registerMarqueeOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n scale,\n onRectChange: (newRect) => {\n rect.value = newRect;\n },\n });\n\n onCleanup(() => {\n unregister?.();\n });\n },\n { immediate: true },\n);\n</script>\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":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","props","__props","slots","useSlots","plugin","selPlugin","documentState","useDocumentState","documentId","rects","ref","boundingRect","placement","actualScale","computed","scale","_a","value","actualRotation","rotation","Rotation","Degree0","shouldRenderMenu","pageIndex","isVisible","selectionMenu","watch","docId","pageIdx","_","onCleanup","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","immediate","onMenuPlacement","newPlacement","buildContext","type","buildMenuPlacement","suggestTop","spaceAbove","_b","spaceBelow","_c","renderSelectionMenu","rect","menuWrapperProps","selected","context","_createElementBlock","_Fragment","key","_createElementVNode","style","_normalizeStyle","left","origin","x","top","y","width","size","height","_openBlock","_renderList","i","background","_createBlock","_unref","CounterRotate","default","_withCtx","_resolveDynamicComponent","_renderSlot","_ctx","$slots","provides","sel","watchEffect","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","unregister","registerMarqueeOnPage","onRectChange","newRect","stroke","fill","class","className","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build"],"mappings":"0QAOaA,EAAyB,IAAMC,gBAA+BC,EAAAA,gBAAgBC,IAM9EC,EAAqB,IAAMC,YAA2BH,EAAAA,gBAAgBC,sMCMnF,MAAMG,EAAQC,EAKRC,EAAQC,EAAAA,YACNC,OAAQC,GAAcP,IACxBQ,EAAgBC,EAAAA,iBAAiB,IAAMP,EAAMQ,YAC7CC,EAAQC,EAAAA,IAAY,IACpBC,EAAeD,EAAAA,IAAiB,MAChCE,EAAYF,EAAAA,IAAmC,MAE/CG,EAAcC,EAAAA,SAAS,WAC3B,YAAoB,IAAhBd,EAAMe,MAA4Bf,EAAMe,OACrC,OAAAC,EAAAV,EAAcW,YAAd,EAAAD,EAAqBD,QAAS,IAGjCG,EAAiBJ,EAAAA,SAAS,WAC9B,YAAuB,IAAnBd,EAAMmB,SAA+BnB,EAAMmB,UACxC,OAAAH,EAAAV,EAAcW,YAAd,EAAAD,EAAqBG,WAAYC,EAAAA,SAASC,UAI7CC,EAAmBR,EAAAA,SAAS,MAC3BF,EAAUK,QACXL,EAAUK,MAAMM,YAAcvB,EAAMuB,cACnCX,EAAUK,MAAMO,cAGZxB,EAAMyB,iBAAmBvB,EAAM,sBAG1CwB,EAAAA,MACE,CAAC,IAAMrB,EAAUY,MAAO,IAAMjB,EAAMQ,WAAY,IAAMR,EAAMuB,WAC5D,EAAEnB,EAAQuB,EAAOC,GAAUC,EAAGC,KAC5B,IAAK1B,IAAWuB,EAGd,OAFAlB,EAAMQ,MAAQ,QACdN,EAAaM,MAAQ,MAavBa,EATmB1B,EAAO2B,wBAAwB,CAChDvB,WAAYmB,EACZJ,UAAWK,EACXI,cAAe,EAAGvB,MAAOwB,EAAUtB,aAAcuB,MAC/CzB,EAAMQ,MAAQgB,EACdtB,EAAaM,MAAQiB,OAM3B,CAAEC,WAAW,IAGfT,EAAAA,MACE,CAAC,IAAMrB,EAAUY,MAAO,IAAMjB,EAAMQ,YACpC,EAAEJ,EAAQuB,GAAQE,EAAGC,KACnB,IAAK1B,IAAWuB,EAEd,YADAf,EAAUK,MAAQ,MAQpBa,EAJoB1B,EAAOgC,gBAAgBT,EAAQU,IACjDzB,EAAUK,MAAQoB,MAKtB,CAAEF,WAAW,IAMf,MAAMG,EAAe,KAAA,CACnBC,KAAM,YACNhB,UAAWvB,EAAMuB,YAIbiB,EAAqB,eAAO,MAAA,CAChCC,YAAY,OAAAzB,EAAAJ,EAAUK,YAAV,EAAAD,EAAiByB,cAAc,EAC3CC,YAAY,OAAAC,EAAA/B,EAAUK,YAAV,EAAA0B,EAAiBD,aAAc,EAC3CE,YAAY,OAAAC,EAAAjC,EAAUK,YAAV,EAAA4B,EAAiBD,aAAc,IAIvCE,EAAsB,CAACC,EAAYC,IAClChD,EAAMyB,cAEJzB,EAAMyB,cAAc,CACzBsB,OACAC,mBACAC,UAAU,EACVrC,UAAW4B,IACXU,QAASZ,MAPsB,kBAajB3B,EAAAM,qBAAhBkC,EAAAA,mBA0DWC,WAAA,CAAAC,IAAA,GAAA,CAzDTC,EAAAA,mBAwBM,MAAA,CAvBHC,MAAKC,EAAAA,eAAA,qBAAmDC,KAAA9C,EAAAM,MAAayC,OAAOC,EAAI9C,EAAAI,MAAxB,KAAyD2C,IAAAjD,EAAAM,MAAayC,OAAOG,EAAIhD,EAAAI,MAAxB,KAA2D6C,MAAAnD,EAAAM,MAAa8C,KAAKD,MAAQjD,EAAAI,MAA1B,KAA8D+C,OAAArD,EAAAM,MAAa8C,KAAKC,OAASnD,EAAAI,MAA3B,2EAW3OgD,EAAAA,WAAA,GAAAd,EAAAA,mBAWEC,WAAA,KAAAc,EAAAA,WAVoBzD,EAAAQ,MAAK,CAAjB8B,EAAMoB,mBADhBhB,EAAAA,mBAWE,MAAA,CATCE,IAAKc,EACLZ,MAAKC,EAAAA,eAAA,2BAAwDT,EAAKW,OAAOC,EAAIhD,EAAAM,MAAayC,OAAOC,GAAK9C,EAAAI,gBAAoC8B,EAAKW,OAAOG,EAAIlD,EAAAM,MAAayC,OAAOG,GAAKhD,EAAAI,WAAqC6C,MAAAf,EAAKgB,KAAKD,MAAQjD,EAAAI,MAAlB,KAAwD+C,OAAAjB,EAAKgB,KAAKC,OAASnD,EAAAI,MAAnB,gBAA0DhB,EAAAmE,kCAavU9C,EAAAL,qBADRoD,EAAAA,YA6BgBC,EAAAA,MAAAC,EAAAA,eAAA,OA3BbxB,KAAI,SAAmCY,EAAA/C,EAAAK,MAAW8B,KAAKW,OAAOC,EAAI9C,EAAAI,MAA0B4C,EAAAjD,EAAAK,MAAW8B,KAAKW,OAAOG,EAAIhD,EAAAI,aAAyD6C,MAAAlD,EAAAK,MAAW8B,KAAKgB,KAAKD,MAAQjD,EAAAI,MAA+B+C,OAAApD,EAAAK,MAAW8B,KAAKgB,KAAKC,OAASnD,EAAAI,QAU1QE,SAAUD,EAAAD,QAEAuD,QAAOC,EAAAA,QAEhB,EAFoB1B,OAAMC,sBAAgB,CAEzB/C,EAAAwB,eAAjBwC,cAAAI,EAAAA,YAAoFK,EAAAA,wBAA/C5B,EAAoBC,EAAMC,IAAgB,CAAAK,IAAA,KAG/EsB,EAAAA,WAQEC,EAAAC,OAAA,iBAAA,OALC3B,QAASZ,IACTW,UAAU,EACVF,OACAnC,UAAW4B,IACXQ,8KC3KX,MAAQ8B,SAAUC,GAAQrF,WAI1BsF,EAAAA,YAAalD,IACX,GAAIiD,EAAI9D,MAAO,CASba,EARoBiD,EAAI9D,MAAMgE,kBAAkB,EAAGC,WAEjDC,UAAUC,UAAUC,UAAUH,GAAMI,MAAOC,IACzCC,QAAQC,MAAM,oCAAqCF,OAMzD,kNCqBF,MAAMvF,EAAQC,GAKNG,OAAQC,GAAcP,IACxBQ,EAAgBC,EAAAA,iBAAiB,IAAMP,EAAMQ,YAC7CuC,EAAOrC,EAAAA,IAAiB,MAExBG,EAAcC,EAAAA,SAAS,WAC3B,YAAoB,IAAhBd,EAAMe,MAA4Bf,EAAMe,OACrC,OAAAC,EAAAV,EAAcW,YAAd,EAAAD,EAAqBD,QAAS,WAGvCW,EAAAA,MACE,CAACrB,EAAW,IAAML,EAAMQ,WAAY,IAAMR,EAAMuB,UAAWV,GAC3D,EAAET,EAAQuB,EAAOC,EAASb,GAAQc,EAAGC,KAGnC,GAFAiB,EAAK9B,MAAQ,MAERb,EACH,OAGF,MAAMsF,EAAatF,EAAOuF,sBAAsB,CAC9CnF,WAAYmB,EACZJ,UAAWK,EACXb,QACA6E,aAAeC,IACb9C,EAAK9B,MAAQ4E,KAIjB/D,EAAU,KACR,MAAA4D,GAAAA,OAGJ,CAAEvD,WAAW,WA1ELY,EAAA9B,qBADRkC,EAAAA,mBAeE,MAAA,OAbCI,MAAKC,EAAAA,eAAA,0CAA4EC,KAAAV,EAAA9B,MAAKyC,OAAOC,EAAI9C,EAAAI,MAAhB,KAA+C2C,IAAAb,EAAA9B,MAAKyC,OAAOG,EAAIhD,EAAAI,MAAhB,KAAiD6C,MAAAf,EAAA9B,MAAK8C,KAAKD,MAAQjD,EAAAI,MAAlB,KAAoD+C,OAAAjB,EAAA9B,MAAK8C,KAAKC,OAASnD,EAAAI,MAAnB,0BAAgEhB,EAAA6F,oBAA4B7F,EAAA8F,yCAYjUC,uBAAO/F,EAAAgG,oDCLCC,EAAyBC,EAAAA,oBAAoBC,EAAAA,wBACvDC,WAAWC,GACXC"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/vue/hooks/use-selection.ts","../../src/vue/components/selection-layer.vue","../../src/vue/components/copy-to-clipboard.vue","../../src/vue/components/marquee-selection.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 page = computed(() => documentState.value?.document?.pages?.[props.pageIndex]);\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 // Combine page intrinsic rotation with document rotation\n const pageRotation = page.value?.rotation ?? 0;\n const docRotation = documentState.value?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\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","<template>\n <div\n v-if=\"rect\"\n :style=\"{\n position: 'absolute',\n pointerEvents: 'none',\n left: `${rect.origin.x * actualScale}px`,\n top: `${rect.origin.y * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }\"\n :class=\"className\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport type { Rect } from '@embedpdf/models';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\nconst props = withDefaults(defineProps<MarqueeSelectionProps>(), {\n stroke: 'rgba(0,122,204,0.8)',\n fill: 'rgba(0,122,204,0.15)',\n});\n\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst rect = ref<Rect | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nwatch(\n [selPlugin, () => props.documentId, () => props.pageIndex, actualScale],\n ([plugin, docId, pageIdx, scale], _, onCleanup) => {\n rect.value = null;\n\n if (!plugin) {\n return;\n }\n\n const unregister = plugin.registerMarqueeOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n scale,\n onRectChange: (newRect) => {\n rect.value = newRect;\n },\n });\n\n onCleanup(() => {\n unregister?.();\n });\n },\n { immediate: true },\n);\n</script>\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":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","props","__props","slots","useSlots","plugin","selPlugin","documentState","useDocumentState","documentId","page","computed","_c","_b","_a","value","document","pages","pageIndex","rects","ref","boundingRect","placement","actualScale","scale","actualRotation","rotation","shouldRenderMenu","isVisible","selectionMenu","watch","docId","pageIdx","_","onCleanup","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","immediate","onMenuPlacement","newPlacement","buildContext","type","buildMenuPlacement","suggestTop","spaceAbove","spaceBelow","renderSelectionMenu","rect","menuWrapperProps","selected","context","_createElementBlock","_Fragment","key","_createElementVNode","style","_normalizeStyle","left","origin","x","top","y","width","size","height","_openBlock","_renderList","i","background","_createBlock","_unref","CounterRotate","default","_withCtx","_resolveDynamicComponent","_renderSlot","_ctx","$slots","provides","sel","watchEffect","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","unregister","registerMarqueeOnPage","onRectChange","newRect","stroke","fill","class","className","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build"],"mappings":"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"}
package/dist/vue/index.js CHANGED
@@ -1,7 +1,7 @@
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, useSlots, ref, computed, watch, createElementBlock, createCommentVNode, openBlock, Fragment, createElementVNode, createBlock, normalizeStyle, renderList, unref, withCtx, renderSlot, resolveDynamicComponent, watchEffect, normalizeClass } from "vue";
4
+ import { defineComponent, useSlots, computed, ref, watch, createElementBlock, createCommentVNode, openBlock, Fragment, createElementVNode, createBlock, normalizeStyle, renderList, unref, withCtx, renderSlot, resolveDynamicComponent, watchEffect, normalizeClass } from "vue";
5
5
  import { usePlugin, useCapability, useDocumentState } from "@embedpdf/core/vue";
6
6
  import { Rotation } from "@embedpdf/models";
7
7
  import { CounterRotate } from "@embedpdf/utils/vue";
@@ -22,6 +22,10 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
22
22
  const slots = useSlots();
23
23
  const { plugin: selPlugin } = useSelectionPlugin();
24
24
  const documentState = useDocumentState(() => props.documentId);
25
+ const page = computed(() => {
26
+ var _a, _b, _c;
27
+ return (_c = (_b = (_a = documentState.value) == null ? void 0 : _a.document) == null ? void 0 : _b.pages) == null ? void 0 : _c[props.pageIndex];
28
+ });
25
29
  const rects = ref([]);
26
30
  const boundingRect = ref(null);
27
31
  const placement = ref(null);
@@ -31,9 +35,11 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
31
35
  return ((_a = documentState.value) == null ? void 0 : _a.scale) ?? 1;
32
36
  });
33
37
  const actualRotation = computed(() => {
34
- var _a;
38
+ var _a, _b;
35
39
  if (props.rotation !== void 0) return props.rotation;
36
- return ((_a = documentState.value) == null ? void 0 : _a.rotation) ?? Rotation.Degree0;
40
+ const pageRotation = ((_a = page.value) == null ? void 0 : _a.rotation) ?? 0;
41
+ const docRotation = ((_b = documentState.value) == null ? void 0 : _b.rotation) ?? 0;
42
+ return (pageRotation + docRotation) % 4;
37
43
  });
38
44
  const shouldRenderMenu = computed(() => {
39
45
  if (!placement.value) return false;
@@ -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/components/marquee-selection.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","<template>\n <div\n v-if=\"rect\"\n :style=\"{\n position: 'absolute',\n pointerEvents: 'none',\n left: `${rect.origin.x * actualScale}px`,\n top: `${rect.origin.y * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }\"\n :class=\"className\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport type { Rect } from '@embedpdf/models';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\nconst props = withDefaults(defineProps<MarqueeSelectionProps>(), {\n stroke: 'rgba(0,122,204,0.8)',\n fill: 'rgba(0,122,204,0.15)',\n});\n\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst rect = ref<Rect | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nwatch(\n [selPlugin, () => props.documentId, () => props.pageIndex, actualScale],\n ([plugin, docId, pageIdx, scale], _, onCleanup) => {\n rect.value = null;\n\n if (!plugin) {\n return;\n }\n\n const unregister = plugin.registerMarqueeOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n scale,\n onRectChange: (newRect) => {\n rect.value = newRect;\n },\n });\n\n onCleanup(() => {\n unregister?.();\n });\n },\n { immediate: true },\n);\n</script>\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":"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"}
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/components/marquee-selection.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 page = computed(() => documentState.value?.document?.pages?.[props.pageIndex]);\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 // Combine page intrinsic rotation with document rotation\n const pageRotation = page.value?.rotation ?? 0;\n const docRotation = documentState.value?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\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","<template>\n <div\n v-if=\"rect\"\n :style=\"{\n position: 'absolute',\n pointerEvents: 'none',\n left: `${rect.origin.x * actualScale}px`,\n top: `${rect.origin.y * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }\"\n :class=\"className\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport type { Rect } from '@embedpdf/models';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\nconst props = withDefaults(defineProps<MarqueeSelectionProps>(), {\n stroke: 'rgba(0,122,204,0.8)',\n fill: 'rgba(0,122,204,0.15)',\n});\n\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst rect = ref<Rect | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nwatch(\n [selPlugin, () => props.documentId, () => props.pageIndex, actualScale],\n ([plugin, docId, pageIdx, scale], _, onCleanup) => {\n rect.value = null;\n\n if (!plugin) {\n return;\n }\n\n const unregister = plugin.registerMarqueeOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n scale,\n onRectChange: (newRect) => {\n rect.value = newRect;\n },\n });\n\n onCleanup(() => {\n unregister?.();\n });\n },\n { immediate: true },\n);\n</script>\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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embedpdf/plugin-selection",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.cjs",
@@ -34,17 +34,17 @@
34
34
  }
35
35
  },
36
36
  "dependencies": {
37
- "@embedpdf/models": "2.4.0",
38
- "@embedpdf/utils": "2.4.0"
37
+ "@embedpdf/models": "2.5.0",
38
+ "@embedpdf/utils": "2.5.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/react": "^18.2.0",
42
42
  "typescript": "^5.0.0",
43
- "@embedpdf/core": "2.4.0",
44
43
  "@embedpdf/build": "1.1.0",
45
- "@embedpdf/plugin-viewport": "2.4.0",
46
- "@embedpdf/plugin-interaction-manager": "2.4.0",
47
- "@embedpdf/plugin-scroll": "2.4.0"
44
+ "@embedpdf/core": "2.5.0",
45
+ "@embedpdf/plugin-interaction-manager": "2.5.0",
46
+ "@embedpdf/plugin-viewport": "2.5.0",
47
+ "@embedpdf/plugin-scroll": "2.5.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": ">=16.8.0",
@@ -52,8 +52,8 @@
52
52
  "preact": "^10.26.4",
53
53
  "vue": ">=3.2.0",
54
54
  "svelte": ">=5 <6",
55
- "@embedpdf/core": "2.4.0",
56
- "@embedpdf/plugin-interaction-manager": "2.4.0"
55
+ "@embedpdf/core": "2.5.0",
56
+ "@embedpdf/plugin-interaction-manager": "2.5.0"
57
57
  },
58
58
  "files": [
59
59
  "dist",