@embedpdf/react 3.0.0-next.15 → 3.0.0-next.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AnnotationList-CkijRHet-HEFNUIZe.d.ts → AnnotationList-DcyhF2kU-CiBFOq1z.d.cts} +5 -4
- package/dist/{AnnotationList-CkijRHet-HEFNUIZe.d.cts → AnnotationList-DcyhF2kU-CiBFOq1z.d.ts} +5 -4
- package/dist/actions.cjs +2 -2
- package/dist/actions.js +2 -2
- package/dist/{annotation-C9dVwo9b.d.cts → annotation-3UvABfJm.d.cts} +5 -2
- package/dist/{annotation-Dex5EBda.cjs → annotation-BNHQpMKv.cjs} +11 -35
- package/dist/annotation-BNHQpMKv.cjs.map +1 -0
- package/dist/{annotation-C1ttM8Wj.js → annotation-CDI8CFb3.js} +12 -36
- package/dist/annotation-CDI8CFb3.js.map +1 -0
- package/dist/{annotation-B48UT2ln.d.ts → annotation-DhEfs0pH.d.ts} +5 -2
- package/dist/annotation.cjs +1 -1
- package/dist/annotation.d.cts +1 -1
- package/dist/annotation.d.ts +1 -1
- package/dist/annotation.js +1 -1
- package/dist/attachment.cjs +39 -0
- package/dist/attachment.cjs.map +1 -0
- package/dist/attachment.d.cts +29 -0
- package/dist/attachment.d.ts +29 -0
- package/dist/attachment.js +28 -0
- package/dist/attachment.js.map +1 -0
- package/dist/commands.cjs +2 -2
- package/dist/commands.js +2 -2
- package/dist/{form-55izw0jS.js → form-BSGvjA5D.js} +3 -3
- package/dist/{form-55izw0jS.js.map → form-BSGvjA5D.js.map} +1 -1
- package/dist/{form-BNqQfGGH.cjs → form-BgztZyOK.cjs} +3 -3
- package/dist/{form-BNqQfGGH.cjs.map → form-BgztZyOK.cjs.map} +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.d.cts +1 -1
- package/dist/form.d.ts +1 -1
- package/dist/form.js +1 -1
- package/dist/history.cjs +2 -2
- package/dist/history.js +2 -2
- package/dist/i18n.cjs +2 -2
- package/dist/i18n.js +2 -2
- package/dist/index.cjs +32 -5
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +11 -7
- package/dist/interaction.cjs +2 -2
- package/dist/interaction.js +2 -2
- package/dist/link.cjs +1 -1
- package/dist/link.js +1 -1
- package/dist/measurement.cjs +2 -2
- package/dist/measurement.js +2 -2
- package/dist/metadata.cjs +2 -2
- package/dist/metadata.js +2 -2
- package/dist/outline.cjs +39 -0
- package/dist/outline.cjs.map +1 -0
- package/dist/outline.d.cts +31 -0
- package/dist/outline.d.ts +31 -0
- package/dist/outline.js +28 -0
- package/dist/outline.js.map +1 -0
- package/dist/page-edit.cjs +9 -1
- package/dist/page-edit.cjs.map +1 -1
- package/dist/page-edit.d.cts +12 -1
- package/dist/page-edit.d.ts +12 -1
- package/dist/page-edit.js +10 -3
- package/dist/page-edit.js.map +1 -1
- package/dist/page-view.cjs +1 -1
- package/dist/page-view.js +1 -1
- package/dist/redaction.cjs +2 -2
- package/dist/redaction.d.cts +1 -1
- package/dist/redaction.d.ts +1 -1
- package/dist/redaction.js +2 -2
- package/dist/render.cjs +2 -2
- package/dist/render.js +2 -2
- package/dist/{runtime-fzpGv78v.cjs → runtime-BfnpyfY0.cjs} +3 -2
- package/dist/runtime-BfnpyfY0.cjs.map +1 -0
- package/dist/{runtime-B6xcPsEb.js → runtime-tD5iJU83.js} +4 -3
- package/dist/runtime-tD5iJU83.js.map +1 -0
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +1 -1
- package/dist/search.cjs +2 -2
- package/dist/search.d.cts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +2 -2
- package/dist/selection.cjs +2 -2
- package/dist/selection.js +2 -2
- package/dist/shell.cjs +2 -2
- package/dist/shell.js +2 -2
- package/dist/signature.cjs +2 -2
- package/dist/signature.d.cts +1 -1
- package/dist/signature.d.ts +1 -1
- package/dist/signature.js +2 -2
- package/dist/{stage-BIG6aeVd.cjs → stage-B7BG2e8Q.cjs} +2 -2
- package/dist/{stage-BIG6aeVd.cjs.map → stage-B7BG2e8Q.cjs.map} +1 -1
- package/dist/{stage-DoVekb83.js → stage-BqfBjDu6.js} +2 -2
- package/dist/{stage-DoVekb83.js.map → stage-BqfBjDu6.js.map} +1 -1
- package/dist/stage.cjs +1 -1
- package/dist/stage.js +1 -1
- package/dist/stamp.cjs +2 -2
- package/dist/stamp.js +2 -2
- package/dist/{state-CbgxhwLA.js → state-CzI2KlZb.js} +2 -2
- package/dist/{state-CbgxhwLA.js.map → state-CzI2KlZb.js.map} +1 -1
- package/dist/{state-CsfZy_Eq.cjs → state-Dtf8HAue.cjs} +2 -2
- package/dist/{state-CsfZy_Eq.cjs.map → state-Dtf8HAue.cjs.map} +1 -1
- package/dist/toolbar.cjs +1 -1
- package/dist/toolbar.js +1 -1
- package/dist/view-manager.cjs +2 -2
- package/dist/view-manager.js +2 -2
- package/package.json +40 -30
- package/dist/annotation-C1ttM8Wj.js.map +0 -1
- package/dist/annotation-Dex5EBda.cjs.map +0 -1
- package/dist/runtime-B6xcPsEb.js.map +0 -1
- package/dist/runtime-fzpGv78v.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage-BIG6aeVd.cjs","names":["useStageToken","useDocumentScope","useKernelValue","useCapability","useViewerSettings","PageProvider","useStageToken","useCapability","useOptionalCapability","InteractionToken","InteractionPublicToken","useSelector","useKernelValue","useDocumentId","StageScope","ShownPagesProvider","ProjectorProvider","useDocumentScope","stageState","shallowEqual","DEFAULT_SETTINGS"],"sources":["../src/scrollbar.tsx","../src/stage.tsx"],"sourcesContent":["/**\n * Headless scrollbar for a Stage lens — a pure view of `stage.getScrollMetrics()`.\n *\n * The Stage exposes the native scroller contract (scrollTop/scrollHeight/\n * clientHeight, in screen px — see plugin-stage's README); this component turns\n * it into a native-feeling bar: thumb drag with pointer capture and a preserved\n * grab point, track paging with press-and-hold repeat that stops at the\n * pointer, a minimum thumb length on long documents, and macOS-style overlay\n * auto-hide. On an unbounded stage the metrics already carry the Figma\n * semantics (the range is the union of content and view), so the same bar\n * shrinks toward the edge as you pan away and rides you back — the mapping is\n * frozen for the duration of a thumb drag so the thumb never chases itself\n * while the union re-collapses.\n *\n * Headless styling contract: geometry and behavior live here; looks live in\n * your CSS. Style via `className`/`thumbClassName` (or the style props) against\n * [data-embedpdf-scrollbar][data-axis=\"y\"][data-state=\"visible|hidden\"]\n * [data-embedpdf-scrollbar][data-dragging]\n * [data-embedpdf-scrollbar-thumb]\n * The defaults are deliberately minimal: a transparent track pinned to the\n * viewport edge and a `var(--epdf-scrollbar-thumb, …)` pill, so the bar works\n * unstyled and disappears into any theme. Build something else entirely\n * (minimap, progress ring) from `useScrollMetrics()` + `stage.scrollTo()`.\n */\n\nimport * as React from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { StageCapability } from '@embedpdf/plugin-stage/contract';\nimport type { ScrollMetrics, StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { createScrollbarPresses, paintDefault, scrollbarLayout } from '@embedpdf/web';\nimport type { ScrollbarTrack } from '@embedpdf/web';\nimport { useCapability, useDocumentScope, useKernelValue } from './runtime';\nimport { useStageToken } from './stage-scope';\n\nexport type ScrollbarAxis = 'x' | 'y';\n\n/** What a view with nothing to scroll measures: no document, or not placed yet. */\nconst NO_SCROLL: ScrollMetrics = Object.freeze({\n scrollLeft: 0,\n scrollTop: 0,\n scrollWidth: 0,\n scrollHeight: 0,\n clientWidth: 0,\n clientHeight: 0,\n scrollableX: false,\n scrollableY: false,\n});\n\n/**\n * The view's scroll position as a scrolling element has it: `scrollTop`,\n * `scrollHeight`, `clientHeight` and their horizontal twins, in screen pixels.\n * The raw material for scroll UI of your own; all zero without a document.\n */\nexport function useScrollMetrics(token?: CapabilityToken<StageCapability>): ScrollMetrics {\n const lens = asHost(useStageToken(token));\n const scoped = useDocumentScope();\n // The capability keeps the same metrics object until a number moves.\n return useKernelValue(\n (kernel) => kernel.tryCapability(lens, scoped ?? undefined)?.getScrollMetrics() ?? NO_SCROLL,\n );\n}\n\n// Scroll metrics live on the host lens (the same runtime token, typed wider):\n// a scrollbar is chrome that drives the camera, not a document-level consumer.\nconst asHost = (token: CapabilityToken<StageCapability>) =>\n token as unknown as CapabilityToken<StageHostCapability>;\n\nexport interface ScrollbarProps {\n axis: ScrollbarAxis;\n /** The stage lens to scroll (default: the nearest `<StageScope>` / `<Stage>`, else the main StageToken). */\n token?: CapabilityToken<StageCapability>;\n /**\n * Overlay auto-hide: fade `autoHide` ms after the camera stops moving\n * (drags and hover pin it visible; a hidden bar ignores the pointer, like\n * macOS). `false` = always visible. Visibility is published as\n * `data-state=\"visible|hidden\"` — the default style fades opacity; replace\n * it in CSS for any other treatment. Default 1200.\n */\n autoHide?: number | false;\n /** Minimum thumb length in px — native bars never vanish on long documents.\n * Position stays proportional via the standard travel remap. Default 24. */\n minThumbSize?: number;\n /**\n * Pressing the track: 'page' steps by 90% of a viewport toward the pointer,\n * repeats while held, and stops when the thumb reaches the pointer (the\n * native default); 'jump' centers the thumb at the pointer and hands off to\n * a drag (the macOS option-click / Figma feel). Default 'page'.\n */\n trackPress?: 'page' | 'jump';\n /** Track class/style. The default style pins the bar to the right (y) or\n * bottom (x) edge of the nearest positioned ancestor — the Stage container\n * when rendered in its `overlay`; override to place it anywhere else. */\n className?: string;\n style?: React.CSSProperties;\n thumbClassName?: string;\n thumbStyle?: React.CSSProperties;\n}\n\nexport function Scrollbar({\n axis,\n token: explicitToken,\n autoHide = 1200,\n minThumbSize = 24,\n trackPress = 'page',\n className,\n style,\n thumbClassName,\n thumbStyle,\n}: ScrollbarProps) {\n const token = useStageToken(explicitToken);\n const stage = useCapability(asHost(token));\n const metrics = useScrollMetrics(token);\n const vertical = axis === 'y';\n const scrollable = vertical ? metrics.scrollableY : metrics.scrollableX;\n\n const trackRef = useRef<HTMLDivElement>(null);\n const [trackPx, setTrackPx] = useState(0);\n const [dragging, setDragging] = useState(false);\n const [hovered, setHovered] = useState(false);\n const [active, setActive] = useState(true); // camera moved recently\n\n // The thumb along the track: one formula for drawing, dragging and paging, shared with every framework.\n const layout = scrollbarLayout(metrics, vertical, trackPx, minThumbSize);\n\n // ── overlay auto-hide: any metrics change re-arms the fade timer ──────────\n const hideAfter = autoHide === false ? 0 : autoHide;\n useEffect(() => {\n if (!hideAfter) return;\n setActive(true);\n const timer = setTimeout(() => setActive(false), hideAfter);\n return () => clearTimeout(timer);\n }, [metrics, hideAfter]);\n const shown = !hideAfter || active || hovered || dragging;\n\n // ── track measurement (px mapping needs real geometry) ────────────────────\n useEffect(() => {\n const element = trackRef.current;\n if (!element) return;\n const measure = () => setTrackPx(vertical ? element.clientHeight : element.clientWidth);\n const ro = new ResizeObserver(measure);\n ro.observe(element);\n measure();\n return () => ro.disconnect();\n }, [vertical, scrollable]);\n\n // ── interactions: `@embedpdf/web`'s press handling (thumb drags, track paging and jumps),\n // one per bar, handed the bar as this render has it on every event. ──\n const presses = useMemo(() => createScrollbarPresses(setDragging), []);\n const track = (): ScrollbarTrack => ({\n stage,\n vertical,\n element: trackRef.current!,\n layout,\n // Fresh geometry for paging steps — read from the capability, never a stale render.\n liveLayout: () => scrollbarLayout(stage.getScrollMetrics(), vertical, trackPx, minThumbSize),\n });\n\n useEffect(() => () => presses.release(), [presses]); // unmount: no orphaned repeat timers\n\n if (!scrollable) return null;\n\n const trackDefaults: React.CSSProperties = vertical\n ? { position: 'absolute', top: 0, right: 0, bottom: 0, width: 12 }\n : { position: 'absolute', left: 0, right: 0, bottom: 0, height: 12 };\n const thumbDefaults: React.CSSProperties = vertical\n ? {\n position: 'absolute',\n left: 2,\n right: 2,\n top: layout.thumbPosition,\n height: layout.thumbLength,\n }\n : {\n position: 'absolute',\n top: 2,\n bottom: 2,\n left: layout.thumbPosition,\n width: layout.thumbLength,\n };\n\n return (\n <div\n ref={trackRef}\n role=\"scrollbar\"\n aria-orientation={vertical ? 'vertical' : 'horizontal'}\n aria-valuemin={0}\n aria-valuemax={Math.round(layout.maxOffset)}\n aria-valuenow={Math.round(layout.offset)}\n data-embedpdf-scrollbar=\"\"\n data-axis={axis}\n data-state={shown ? 'visible' : 'hidden'}\n data-dragging={dragging ? '' : undefined}\n className={className}\n style={{\n ...trackDefaults,\n touchAction: 'none',\n userSelect: 'none',\n opacity: shown ? 1 : 0,\n transition: 'opacity 200ms',\n // a hidden overlay bar must not eat the clicks under it\n pointerEvents: shown ? 'auto' : 'none',\n ...style,\n }}\n // Pointer capture retargets to the pressed element and bubbles here — one\n // move/up pair serves thumb drags, jump-drags, and paging alike.\n onPointerDown={(event) => presses.pressTrack(event, track(), trackPress)}\n onPointerMove={(event) => presses.move(event, track())}\n onPointerUp={() => presses.release()}\n onPointerCancel={() => presses.release()}\n onPointerEnter={() => setHovered(true)}\n onPointerLeave={() => setHovered(false)}\n >\n <div\n data-embedpdf-scrollbar-thumb=\"\"\n className={thumbClassName}\n style={{\n ...thumbDefaults,\n borderRadius: 6,\n background: paintDefault('scrollbar-thumb'),\n ...thumbStyle,\n }}\n onPointerDown={(event) => presses.pressThumb(event, track())}\n />\n </div>\n );\n}\n","/**\n * The Stage and its hooks.\n *\n * <Stage> virtualizes and positions page surfaces by the camera, and hands each\n * one to your render prop — you bring the layers. (The standalone, Stage-free\n * single-page surface lives in `./page-view` so it never pulls the stage plugin.)\n * The hooks are the plugin's four: `useStage()` (the API), `useStageState()`,\n * `useStageEvent()` and `useStageSettings()`; `<Scrollbar>` is the Stage's too.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-stage';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport { createScrollHandler, DEFAULT_SETTINGS, stageState } from '@embedpdf/plugin-stage';\nimport type { StageCapability, StageSettings, VisiblePage } from '@embedpdf/plugin-stage';\nimport type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport { shallowEqual, toPageRef } from '@embedpdf/core';\nimport type { CapabilityToken, EventHook } from '@embedpdf/core';\nimport type { PageFrame } from '@embedpdf/core-geometry';\nimport { InteractionToken as InteractionPublicToken } from '@embedpdf/plugin-interaction/contract';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';\nimport {\n createStageSurface,\n makePageContext,\n pageSurfaceLayout,\n paint,\n stagePageDemand,\n stageViewProjector,\n} from '@embedpdf/web';\nimport {\n ProjectorProvider,\n ShownPagesProvider,\n type ProjectorBinding,\n type ShownPages,\n type ViewProjector,\n} from './anchored';\nimport {\n PageProvider,\n useCapability,\n useCapabilityEvent,\n useDocumentId,\n useDocumentScope,\n useKernelValue,\n useOptionalCapability,\n useSelector,\n useViewerSettings,\n} from './runtime';\nimport type { PageContextValue } from './runtime';\nimport { StageScope, useStageToken } from './stage-scope';\nimport type { StageTokenProp } from './stage-scope';\n\nexport { StageScope, useStageToken } from './stage-scope';\nexport type { StageScopeProps, StageTokenProp } from './stage-scope';\nexport { Scrollbar, useScrollMetrics } from './scrollbar';\nexport type { ScrollbarAxis, ScrollbarProps } from './scrollbar';\n\nfunction PageSurface({\n documentId,\n page,\n frame,\n stage,\n render,\n chrome,\n}: {\n documentId: string;\n page: VisiblePage;\n /** Reserved chrome bands around the page (screen px); the layout reserved the\n * matching space, so the outer box tiles into it. */\n frame: PageFrame;\n /** The stage capability — the demand getter reads visibility live off it. */\n stage: StageHostCapability;\n render: (page: PageContextValue) => React.ReactNode;\n chrome?: (page: PageContextValue) => React.ReactNode;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n const transform = page.transform;\n // The viewer's `page` settings; `--epdf-page-*` CSS variables win over them.\n const look = useViewerSettings((settings) => settings.page);\n // The outer, shadow and content boxes, all from the transform; `screenX`/`screenY` are the\n // device-snapped footprint's top-left.\n const layout = pageSurfaceLayout(transform, frame, { x: page.screenX, y: page.screenY });\n // The page address this surface hands its layers. The stage rebuilds\n // `VisiblePage.ref` every camera frame, so it is memoized by the number:\n // identity-stable per page, safe for layers to key effects on.\n const pageObjectNumber = page.ref.objectNumber;\n const pageRef = useMemo(() => toPageRef(pageObjectNumber), [pageObjectNumber]);\n // The page-view demand is a pull: the getter closes over stable references\n // (capability + page address) and reads the stage's live state at call time.\n // Absent from the visible set = zero rect (\"want nothing\"), distinct from\n // PageView's undefined getter (\"whole page\").\n const ctx = useMemo(\n () =>\n makePageContext(\n documentId,\n // the hosting lens — per-view raster planning keys tile state by it,\n // so a thumbnail rail and the main view never fight over one plan\n stage.getLensId(),\n pageRef,\n page.pageIndex,\n frame,\n transform,\n () => ref.current!.getBoundingClientRect(),\n () => stagePageDemand(stage, pageObjectNumber, transform.deviceWidth),\n ),\n [documentId, pageRef, pageObjectNumber, page.pageIndex, frame, transform, stage],\n );\n return (\n <div style={{ position: 'absolute', ...layout.outer }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — axis-aligned at the content box (inset by the frame),\n transparent fill so it can never peek out behind the bitmap, and it\n stays put under rotation. */}\n <div\n style={{\n position: 'absolute',\n ...layout.shadow,\n boxShadow: paint('page-shadow', look.shadow),\n }}\n />\n {/* the page: white backing + bitmap as ONE rasterized box, so there is no\n seam between them and nothing white larger than the bitmap to leak.\n The ONLY thing rotation turns; markers/annotations ride it in content\n coordinates. Rotation 0 carries no transform → pixel-snaps cleanly. */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n ...layout.content,\n background: paint('page-background', look.background),\n transform: layout.turn ?? undefined,\n // We render our own selection highlights — suppress native text/image\n // selection (and the double-click image grab) on the whole page subtree.\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {render(ctx)}\n </div>\n {/* box-space chrome — labels, selection border, per-page buttons — fills\n the outer box, NEVER rotates. Bands are plain regions: a label is\n `bottom:0; height: frame.bottom`, a button row `top:0; height: frame.top`. */}\n {chrome?.(ctx)}\n </PageProvider>\n </div>\n );\n}\n\nexport interface StageProps {\n /**\n * Page-space content for each visible page (RenderLayer, annotations,\n * markers). Rendered inside the page's content frame, so it rotates with the\n * page's display rotation — coordinates are plain PDF points.\n */\n children: (page: PageContextValue) => React.ReactNode;\n /**\n * Box-space chrome for each visible page (page-number label, selection\n * border, a per-page rotate/delete button). Rendered into the outer box\n * (content + reserved `pageFrame`), so it does not rotate and the reserved\n * bands are plain regions (`bottom:0; height: page.frame.bottom`). The three\n * coordinate spaces: `children` (page content), `pageChrome` (page box +\n * frame), `overlay` (viewport).\n */\n pageChrome?: (page: PageContextValue) => React.ReactNode;\n /** Viewport-space UI (menus, controls) rendered above the pages. */\n overlay?: React.ReactNode;\n /**\n * The controlled form of the active tool: while set, the interaction hub's\n * active tool follows this value (re-applied when the document changes),\n * and `onToolChange` reports every change so the owner can update it. Omit\n * both for the uncontrolled default (`useInteraction().activateTool`).\n */\n tool?: string;\n /** Fires on every tool change of this stage's document (controlled or not). */\n onToolChange?: (toolId: string) => void;\n /** The stage lens to drive (default: the nearest `<StageScope>`, else the main StageToken). */\n token?: StageTokenProp;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport function Stage({\n children,\n pageChrome,\n overlay,\n tool,\n onToolChange,\n token: explicitToken,\n className,\n style,\n}: StageProps) {\n const token = useStageToken(explicitToken);\n // The surface is a host of the lens: it reports viewport size, drives gestures and\n // reads the lens id. The host contract is the same runtime token, typed wider.\n const stage = useCapability(token as unknown as CapabilityToken<StageHostCapability>);\n const ix = useOptionalCapability(InteractionToken);\n // Controlled tool: the prop is the source of truth whenever it is set.\n useEffect(() => {\n if (tool === undefined || !ix) return;\n if (ix.getActiveToolId() !== tool) ix.activateTool(tool);\n }, [tool, ix]);\n useCapabilityEvent(\n InteractionPublicToken,\n (interaction) => interaction.onToolChanged,\n (event) => onToolChange?.(event.toolId),\n );\n // How this view takes pointer input is its own settings (`interaction`,\n // `panFallback`, `zoomGestures`): a change rebinds the surface below.\n const interaction = useSelector(token, (stage) => stage.getSettings().interaction);\n const panFallback = useSelector(token, (stage) => stage.getSettings().panFallback);\n const zoomGestures = useSelector(token, (stage) => stage.getSettings().zoomGestures);\n // Routing pointer input to the interaction hub (page-resolved) also\n // registers this view's tool-gated pan-scroll handler with it, scoped to\n // the view, so two Stages on one document never pan each other. Without\n // the hub the Stage falls back to its own drag-to-pan.\n const useHub = interaction && !!ix;\n // The hub's resolved cursor (text/grab/…), applied to the viewport when driving.\n const hubCursor = useKernelValue(() => ix?.getCursor() ?? 'default');\n const ref = useRef<HTMLDivElement>(null);\n const docId = useDocumentId();\n // visiblePages already folds in the camera (each page carries its device-snapped\n // screenX/screenY + transform), so panning re-emits the list — no separate\n // camera subscription needed for positioning.\n const pages = useSelector(token, (stage) => stage.listVisiblePages()); // memoized -> stable ref\n // Reserved chrome bands (screen px), uniform across pages — the frame the\n // outer box reserves and `pageChrome` paints into.\n const frame = useSelector(\n token,\n (stage) => stage.getSettings().pageFrame,\n (left, right) =>\n left.top === right.top &&\n left.right === right.right &&\n left.bottom === right.bottom &&\n left.left === right.left,\n );\n\n // The Stage's ViewProjector: anchored UI (menus, popovers) positions through\n // the camera — pure state, no DOM reads, no portal, and no subscription:\n // `pages` (visiblePages) is the binding's revision, so a camera change\n // re-renders the pages and every anchored consumer in the same React\n // commit — surface and overlay can never paint a frame apart.\n const projector = useMemo<ViewProjector>(() => stageViewProjector(() => stage), [stage]);\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: pages }),\n [projector, pages],\n );\n // The pages on screen as one value that changes only when a page comes or\n // goes, so anchored UI on the other pages sits out every camera frame.\n const shownKey = pages.map((page) => page.ref.objectNumber).join(',');\n const shownPages = useMemo<ShownPages>(\n () => new Set(shownKey ? shownKey.split(',').map(Number) : []),\n [shownKey],\n );\n\n useLayoutEffect(() => {\n const element = ref.current!;\n // The whole browser binding — viewport/DPR reporting, sample normalization,\n // gesture controller — is the shared @embedpdf/web surface, so every\n // framework adapter has one feel. This component keeps only React glue.\n const detachSurface = createStageSurface(element, stage, {\n hub: useHub ? ix : null,\n source: stage.getLensId(),\n zoomGestures,\n });\n // Interaction opt-in lives with the sample source: the same knob that\n // forwards this lens's samples also registers its pan-scroll handler,\n // lens-scoped — two stages on one document can never pan each other.\n const offScroll =\n useHub && ix\n ? ix.registerHandler(createScrollHandler(stage, ix, { panFallback }), {\n source: stage.getLensId(),\n })\n : null;\n\n return () => {\n offScroll?.();\n detachSurface();\n };\n }, [stage, ix, useHub, zoomGestures, panFallback]);\n\n return (\n <div\n ref={ref}\n className={className}\n style={{\n position: 'relative',\n overflow: 'hidden',\n touchAction: 'none',\n ...(useHub ? { cursor: hubCursor } : null),\n ...style,\n }}\n >\n {/* Everything inside binds to this lens by default: a `useStageState()` in\n a page's chrome or a `<SelectionHandles>` in the overlay needs no token. */}\n <StageScope token={token}>\n {pages.map((visiblePage) => (\n <PageSurface\n key={visiblePage.ref.objectNumber} // durable page identity — survives move/delete (matches Angular's `track visiblePage.ref.objectNumber`)\n documentId={docId ?? ''}\n page={visiblePage}\n frame={frame}\n stage={stage}\n render={children}\n chrome={pageChrome}\n />\n ))}\n {/* Anchored UI mounts in the overlay: absolute coords here are the\n projector's overlay space (the stage container). */}\n <ShownPagesProvider value={shownPages}>\n <ProjectorProvider value={projectorBinding}>{overlay}</ProjectorProvider>\n </ShownPagesProvider>\n </StageScope>\n </div>\n );\n}\n\n// ── The hooks ────────────────────────────────────────────────────────────────\n// Every hook takes an optional token; without one it binds to the nearest\n// `<StageScope>` / `<Stage>`, else the main view.\n\n/** The Stage's API (zoom, navigation, reveal, settings), for app chrome. */\nexport function useStage(token?: StageTokenProp): StageCapability {\n return useCapability(useStageToken(token));\n}\n\n/** Subscribe to one stage event for the mounted lifetime: `useStageEvent((stage) => stage.onZoomChanged, handler)`. */\nexport function useStageEvent<T>(\n select: (stage: StageCapability) => EventHook<T>,\n handler: (event: T) => void,\n token?: StageTokenProp,\n): void {\n useCapabilityEvent(useStageToken(token), select, handler);\n}\n\n/** What `useStageState()` returns: the Stage page's State table. */\ntype StageStateValue = (typeof stageState)['empty'];\n\n/**\n * The view's state: the zoom, the current page, the page count, the view\n * rotation and the responsive rules that apply (the page's State table,\n * declared once in `stageState`). Takes a selector, and re-renders only when\n * what it returns changes. Without a document it is `stageState.empty`.\n */\nexport function useStageState<Selected = StageStateValue>(\n select?: (state: StageStateValue) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n // Resolved on every read: a document that closes reads as `empty`, never\n // through its closed capability.\n return useKernelValue((kernel) => {\n const stage = kernel.tryCapability(lens, scoped ?? undefined);\n const state = stage ? stageState.read(stage) : stageState.empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n}\n\n/**\n * The view's settings, or the value `select` picks from them. They belong to\n * the view, so without a document there is none yet and this reads the\n * defaults. Change them with `useStage().updateSettings()`.\n */\nexport function useStageSettings<Selected = StageSettings>(\n select?: (settings: StageSettings) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => {\n const settings = kernel.tryCapability(lens, scoped ?? undefined)?.getSettings();\n const value = settings ?? DEFAULT_SETTINGS;\n return select ? select(value) : (value as unknown as Selected);\n }, shallowEqual);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stage-B7BG2e8Q.cjs","names":["useStageToken","useDocumentScope","useKernelValue","useCapability","useViewerSettings","PageProvider","useStageToken","useCapability","useOptionalCapability","InteractionToken","InteractionPublicToken","useSelector","useKernelValue","useDocumentId","StageScope","ShownPagesProvider","ProjectorProvider","useDocumentScope","stageState","shallowEqual","DEFAULT_SETTINGS"],"sources":["../src/scrollbar.tsx","../src/stage.tsx"],"sourcesContent":["/**\n * Headless scrollbar for a Stage lens — a pure view of `stage.getScrollMetrics()`.\n *\n * The Stage exposes the native scroller contract (scrollTop/scrollHeight/\n * clientHeight, in screen px — see plugin-stage's README); this component turns\n * it into a native-feeling bar: thumb drag with pointer capture and a preserved\n * grab point, track paging with press-and-hold repeat that stops at the\n * pointer, a minimum thumb length on long documents, and macOS-style overlay\n * auto-hide. On an unbounded stage the metrics already carry the Figma\n * semantics (the range is the union of content and view), so the same bar\n * shrinks toward the edge as you pan away and rides you back — the mapping is\n * frozen for the duration of a thumb drag so the thumb never chases itself\n * while the union re-collapses.\n *\n * Headless styling contract: geometry and behavior live here; looks live in\n * your CSS. Style via `className`/`thumbClassName` (or the style props) against\n * [data-embedpdf-scrollbar][data-axis=\"y\"][data-state=\"visible|hidden\"]\n * [data-embedpdf-scrollbar][data-dragging]\n * [data-embedpdf-scrollbar-thumb]\n * The defaults are deliberately minimal: a transparent track pinned to the\n * viewport edge and a `var(--epdf-scrollbar-thumb, …)` pill, so the bar works\n * unstyled and disappears into any theme. Build something else entirely\n * (minimap, progress ring) from `useScrollMetrics()` + `stage.scrollTo()`.\n */\n\nimport * as React from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { StageCapability } from '@embedpdf/plugin-stage/contract';\nimport type { ScrollMetrics, StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { createScrollbarPresses, paintDefault, scrollbarLayout } from '@embedpdf/web';\nimport type { ScrollbarTrack } from '@embedpdf/web';\nimport { useCapability, useDocumentScope, useKernelValue } from './runtime';\nimport { useStageToken } from './stage-scope';\n\nexport type ScrollbarAxis = 'x' | 'y';\n\n/** What a view with nothing to scroll measures: no document, or not placed yet. */\nconst NO_SCROLL: ScrollMetrics = Object.freeze({\n scrollLeft: 0,\n scrollTop: 0,\n scrollWidth: 0,\n scrollHeight: 0,\n clientWidth: 0,\n clientHeight: 0,\n scrollableX: false,\n scrollableY: false,\n});\n\n/**\n * The view's scroll position as a scrolling element has it: `scrollTop`,\n * `scrollHeight`, `clientHeight` and their horizontal twins, in screen pixels.\n * The raw material for scroll UI of your own; all zero without a document.\n */\nexport function useScrollMetrics(token?: CapabilityToken<StageCapability>): ScrollMetrics {\n const lens = asHost(useStageToken(token));\n const scoped = useDocumentScope();\n // The capability keeps the same metrics object until a number moves.\n return useKernelValue(\n (kernel) => kernel.tryCapability(lens, scoped ?? undefined)?.getScrollMetrics() ?? NO_SCROLL,\n );\n}\n\n// Scroll metrics live on the host lens (the same runtime token, typed wider):\n// a scrollbar is chrome that drives the camera, not a document-level consumer.\nconst asHost = (token: CapabilityToken<StageCapability>) =>\n token as unknown as CapabilityToken<StageHostCapability>;\n\nexport interface ScrollbarProps {\n axis: ScrollbarAxis;\n /** The stage lens to scroll (default: the nearest `<StageScope>` / `<Stage>`, else the main StageToken). */\n token?: CapabilityToken<StageCapability>;\n /**\n * Overlay auto-hide: fade `autoHide` ms after the camera stops moving\n * (drags and hover pin it visible; a hidden bar ignores the pointer, like\n * macOS). `false` = always visible. Visibility is published as\n * `data-state=\"visible|hidden\"` — the default style fades opacity; replace\n * it in CSS for any other treatment. Default 1200.\n */\n autoHide?: number | false;\n /** Minimum thumb length in px — native bars never vanish on long documents.\n * Position stays proportional via the standard travel remap. Default 24. */\n minThumbSize?: number;\n /**\n * Pressing the track: 'page' steps by 90% of a viewport toward the pointer,\n * repeats while held, and stops when the thumb reaches the pointer (the\n * native default); 'jump' centers the thumb at the pointer and hands off to\n * a drag (the macOS option-click / Figma feel). Default 'page'.\n */\n trackPress?: 'page' | 'jump';\n /** Track class/style. The default style pins the bar to the right (y) or\n * bottom (x) edge of the nearest positioned ancestor — the Stage container\n * when rendered in its `overlay`; override to place it anywhere else. */\n className?: string;\n style?: React.CSSProperties;\n thumbClassName?: string;\n thumbStyle?: React.CSSProperties;\n}\n\nexport function Scrollbar({\n axis,\n token: explicitToken,\n autoHide = 1200,\n minThumbSize = 24,\n trackPress = 'page',\n className,\n style,\n thumbClassName,\n thumbStyle,\n}: ScrollbarProps) {\n const token = useStageToken(explicitToken);\n const stage = useCapability(asHost(token));\n const metrics = useScrollMetrics(token);\n const vertical = axis === 'y';\n const scrollable = vertical ? metrics.scrollableY : metrics.scrollableX;\n\n const trackRef = useRef<HTMLDivElement>(null);\n const [trackPx, setTrackPx] = useState(0);\n const [dragging, setDragging] = useState(false);\n const [hovered, setHovered] = useState(false);\n const [active, setActive] = useState(true); // camera moved recently\n\n // The thumb along the track: one formula for drawing, dragging and paging, shared with every framework.\n const layout = scrollbarLayout(metrics, vertical, trackPx, minThumbSize);\n\n // ── overlay auto-hide: any metrics change re-arms the fade timer ──────────\n const hideAfter = autoHide === false ? 0 : autoHide;\n useEffect(() => {\n if (!hideAfter) return;\n setActive(true);\n const timer = setTimeout(() => setActive(false), hideAfter);\n return () => clearTimeout(timer);\n }, [metrics, hideAfter]);\n const shown = !hideAfter || active || hovered || dragging;\n\n // ── track measurement (px mapping needs real geometry) ────────────────────\n useEffect(() => {\n const element = trackRef.current;\n if (!element) return;\n const measure = () => setTrackPx(vertical ? element.clientHeight : element.clientWidth);\n const ro = new ResizeObserver(measure);\n ro.observe(element);\n measure();\n return () => ro.disconnect();\n }, [vertical, scrollable]);\n\n // ── interactions: `@embedpdf/web`'s press handling (thumb drags, track paging and jumps),\n // one per bar, handed the bar as this render has it on every event. ──\n const presses = useMemo(() => createScrollbarPresses(setDragging), []);\n const track = (): ScrollbarTrack => ({\n stage,\n vertical,\n element: trackRef.current!,\n layout,\n // Fresh geometry for paging steps — read from the capability, never a stale render.\n liveLayout: () => scrollbarLayout(stage.getScrollMetrics(), vertical, trackPx, minThumbSize),\n });\n\n useEffect(() => () => presses.release(), [presses]); // unmount: no orphaned repeat timers\n\n if (!scrollable) return null;\n\n const trackDefaults: React.CSSProperties = vertical\n ? { position: 'absolute', top: 0, right: 0, bottom: 0, width: 12 }\n : { position: 'absolute', left: 0, right: 0, bottom: 0, height: 12 };\n const thumbDefaults: React.CSSProperties = vertical\n ? {\n position: 'absolute',\n left: 2,\n right: 2,\n top: layout.thumbPosition,\n height: layout.thumbLength,\n }\n : {\n position: 'absolute',\n top: 2,\n bottom: 2,\n left: layout.thumbPosition,\n width: layout.thumbLength,\n };\n\n return (\n <div\n ref={trackRef}\n role=\"scrollbar\"\n aria-orientation={vertical ? 'vertical' : 'horizontal'}\n aria-valuemin={0}\n aria-valuemax={Math.round(layout.maxOffset)}\n aria-valuenow={Math.round(layout.offset)}\n data-embedpdf-scrollbar=\"\"\n data-axis={axis}\n data-state={shown ? 'visible' : 'hidden'}\n data-dragging={dragging ? '' : undefined}\n className={className}\n style={{\n ...trackDefaults,\n touchAction: 'none',\n userSelect: 'none',\n opacity: shown ? 1 : 0,\n transition: 'opacity 200ms',\n // a hidden overlay bar must not eat the clicks under it\n pointerEvents: shown ? 'auto' : 'none',\n ...style,\n }}\n // Pointer capture retargets to the pressed element and bubbles here — one\n // move/up pair serves thumb drags, jump-drags, and paging alike.\n onPointerDown={(event) => presses.pressTrack(event, track(), trackPress)}\n onPointerMove={(event) => presses.move(event, track())}\n onPointerUp={() => presses.release()}\n onPointerCancel={() => presses.release()}\n onPointerEnter={() => setHovered(true)}\n onPointerLeave={() => setHovered(false)}\n >\n <div\n data-embedpdf-scrollbar-thumb=\"\"\n className={thumbClassName}\n style={{\n ...thumbDefaults,\n borderRadius: 6,\n background: paintDefault('scrollbar-thumb'),\n ...thumbStyle,\n }}\n onPointerDown={(event) => presses.pressThumb(event, track())}\n />\n </div>\n );\n}\n","/**\n * The Stage and its hooks.\n *\n * <Stage> virtualizes and positions page surfaces by the camera, and hands each\n * one to your render prop — you bring the layers. (The standalone, Stage-free\n * single-page surface lives in `./page-view` so it never pulls the stage plugin.)\n * The hooks are the plugin's four: `useStage()` (the API), `useStageState()`,\n * `useStageEvent()` and `useStageSettings()`; `<Scrollbar>` is the Stage's too.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-stage';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport { createScrollHandler, DEFAULT_SETTINGS, stageState } from '@embedpdf/plugin-stage';\nimport type { StageCapability, StageSettings, VisiblePage } from '@embedpdf/plugin-stage';\nimport type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport { shallowEqual, toPageRef } from '@embedpdf/core';\nimport type { CapabilityToken, EventHook } from '@embedpdf/core';\nimport type { PageFrame } from '@embedpdf/core-geometry';\nimport { InteractionToken as InteractionPublicToken } from '@embedpdf/plugin-interaction/contract';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';\nimport {\n createStageSurface,\n makePageContext,\n pageSurfaceLayout,\n paint,\n stagePageDemand,\n stageViewProjector,\n} from '@embedpdf/web';\nimport {\n ProjectorProvider,\n ShownPagesProvider,\n type ProjectorBinding,\n type ShownPages,\n type ViewProjector,\n} from './anchored';\nimport {\n PageProvider,\n useCapability,\n useCapabilityEvent,\n useDocumentId,\n useDocumentScope,\n useKernelValue,\n useOptionalCapability,\n useSelector,\n useViewerSettings,\n} from './runtime';\nimport type { PageContextValue } from './runtime';\nimport { StageScope, useStageToken } from './stage-scope';\nimport type { StageTokenProp } from './stage-scope';\n\nexport { StageScope, useStageToken } from './stage-scope';\nexport type { StageScopeProps, StageTokenProp } from './stage-scope';\nexport { Scrollbar, useScrollMetrics } from './scrollbar';\nexport type { ScrollbarAxis, ScrollbarProps } from './scrollbar';\n\nfunction PageSurface({\n documentId,\n page,\n frame,\n stage,\n render,\n chrome,\n}: {\n documentId: string;\n page: VisiblePage;\n /** Reserved chrome bands around the page (screen px); the layout reserved the\n * matching space, so the outer box tiles into it. */\n frame: PageFrame;\n /** The stage capability — the demand getter reads visibility live off it. */\n stage: StageHostCapability;\n render: (page: PageContextValue) => React.ReactNode;\n chrome?: (page: PageContextValue) => React.ReactNode;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n const transform = page.transform;\n // The viewer's `page` settings; `--epdf-page-*` CSS variables win over them.\n const look = useViewerSettings((settings) => settings.page);\n // The outer, shadow and content boxes, all from the transform; `screenX`/`screenY` are the\n // device-snapped footprint's top-left.\n const layout = pageSurfaceLayout(transform, frame, { x: page.screenX, y: page.screenY });\n // The page address this surface hands its layers. The stage rebuilds\n // `VisiblePage.ref` every camera frame, so it is memoized by the number:\n // identity-stable per page, safe for layers to key effects on.\n const pageObjectNumber = page.ref.objectNumber;\n const pageRef = useMemo(() => toPageRef(pageObjectNumber), [pageObjectNumber]);\n // The page-view demand is a pull: the getter closes over stable references\n // (capability + page address) and reads the stage's live state at call time.\n // Absent from the visible set = zero rect (\"want nothing\"), distinct from\n // PageView's undefined getter (\"whole page\").\n const ctx = useMemo(\n () =>\n makePageContext(\n documentId,\n // the hosting lens — per-view raster planning keys tile state by it,\n // so a thumbnail rail and the main view never fight over one plan\n stage.getLensId(),\n pageRef,\n page.pageIndex,\n frame,\n transform,\n () => ref.current!.getBoundingClientRect(),\n () => stagePageDemand(stage, pageObjectNumber, transform.deviceWidth),\n ),\n [documentId, pageRef, pageObjectNumber, page.pageIndex, frame, transform, stage],\n );\n return (\n <div style={{ position: 'absolute', ...layout.outer }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — axis-aligned at the content box (inset by the frame),\n transparent fill so it can never peek out behind the bitmap, and it\n stays put under rotation. */}\n <div\n style={{\n position: 'absolute',\n ...layout.shadow,\n boxShadow: paint('page-shadow', look.shadow),\n }}\n />\n {/* the page: white backing + bitmap as ONE rasterized box, so there is no\n seam between them and nothing white larger than the bitmap to leak.\n The ONLY thing rotation turns; markers/annotations ride it in content\n coordinates. Rotation 0 carries no transform → pixel-snaps cleanly. */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n ...layout.content,\n background: paint('page-background', look.background),\n transform: layout.turn ?? undefined,\n // We render our own selection highlights — suppress native text/image\n // selection (and the double-click image grab) on the whole page subtree.\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {render(ctx)}\n </div>\n {/* box-space chrome — labels, selection border, per-page buttons — fills\n the outer box, NEVER rotates. Bands are plain regions: a label is\n `bottom:0; height: frame.bottom`, a button row `top:0; height: frame.top`. */}\n {chrome?.(ctx)}\n </PageProvider>\n </div>\n );\n}\n\nexport interface StageProps {\n /**\n * Page-space content for each visible page (RenderLayer, annotations,\n * markers). Rendered inside the page's content frame, so it rotates with the\n * page's display rotation — coordinates are plain PDF points.\n */\n children: (page: PageContextValue) => React.ReactNode;\n /**\n * Box-space chrome for each visible page (page-number label, selection\n * border, a per-page rotate/delete button). Rendered into the outer box\n * (content + reserved `pageFrame`), so it does not rotate and the reserved\n * bands are plain regions (`bottom:0; height: page.frame.bottom`). The three\n * coordinate spaces: `children` (page content), `pageChrome` (page box +\n * frame), `overlay` (viewport).\n */\n pageChrome?: (page: PageContextValue) => React.ReactNode;\n /** Viewport-space UI (menus, controls) rendered above the pages. */\n overlay?: React.ReactNode;\n /**\n * The controlled form of the active tool: while set, the interaction hub's\n * active tool follows this value (re-applied when the document changes),\n * and `onToolChange` reports every change so the owner can update it. Omit\n * both for the uncontrolled default (`useInteraction().activateTool`).\n */\n tool?: string;\n /** Fires on every tool change of this stage's document (controlled or not). */\n onToolChange?: (toolId: string) => void;\n /** The stage lens to drive (default: the nearest `<StageScope>`, else the main StageToken). */\n token?: StageTokenProp;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport function Stage({\n children,\n pageChrome,\n overlay,\n tool,\n onToolChange,\n token: explicitToken,\n className,\n style,\n}: StageProps) {\n const token = useStageToken(explicitToken);\n // The surface is a host of the lens: it reports viewport size, drives gestures and\n // reads the lens id. The host contract is the same runtime token, typed wider.\n const stage = useCapability(token as unknown as CapabilityToken<StageHostCapability>);\n const ix = useOptionalCapability(InteractionToken);\n // Controlled tool: the prop is the source of truth whenever it is set.\n useEffect(() => {\n if (tool === undefined || !ix) return;\n if (ix.getActiveToolId() !== tool) ix.activateTool(tool);\n }, [tool, ix]);\n useCapabilityEvent(\n InteractionPublicToken,\n (interaction) => interaction.onToolChanged,\n (event) => onToolChange?.(event.toolId),\n );\n // How this view takes pointer input is its own settings (`interaction`,\n // `panFallback`, `zoomGestures`): a change rebinds the surface below.\n const interaction = useSelector(token, (stage) => stage.getSettings().interaction);\n const panFallback = useSelector(token, (stage) => stage.getSettings().panFallback);\n const zoomGestures = useSelector(token, (stage) => stage.getSettings().zoomGestures);\n // Routing pointer input to the interaction hub (page-resolved) also\n // registers this view's tool-gated pan-scroll handler with it, scoped to\n // the view, so two Stages on one document never pan each other. Without\n // the hub the Stage falls back to its own drag-to-pan.\n const useHub = interaction && !!ix;\n // The hub's resolved cursor (text/grab/…), applied to the viewport when driving.\n const hubCursor = useKernelValue(() => ix?.getCursor() ?? 'default');\n const ref = useRef<HTMLDivElement>(null);\n const docId = useDocumentId();\n // visiblePages already folds in the camera (each page carries its device-snapped\n // screenX/screenY + transform), so panning re-emits the list — no separate\n // camera subscription needed for positioning.\n const pages = useSelector(token, (stage) => stage.listVisiblePages()); // memoized -> stable ref\n // Reserved chrome bands (screen px), uniform across pages — the frame the\n // outer box reserves and `pageChrome` paints into.\n const frame = useSelector(\n token,\n (stage) => stage.getSettings().pageFrame,\n (left, right) =>\n left.top === right.top &&\n left.right === right.right &&\n left.bottom === right.bottom &&\n left.left === right.left,\n );\n\n // The Stage's ViewProjector: anchored UI (menus, popovers) positions through\n // the camera — pure state, no DOM reads, no portal, and no subscription:\n // `pages` (visiblePages) is the binding's revision, so a camera change\n // re-renders the pages and every anchored consumer in the same React\n // commit — surface and overlay can never paint a frame apart.\n const projector = useMemo<ViewProjector>(() => stageViewProjector(() => stage), [stage]);\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: pages }),\n [projector, pages],\n );\n // The pages on screen as one value that changes only when a page comes or\n // goes, so anchored UI on the other pages sits out every camera frame.\n const shownKey = pages.map((page) => page.ref.objectNumber).join(',');\n const shownPages = useMemo<ShownPages>(\n () => new Set(shownKey ? shownKey.split(',').map(Number) : []),\n [shownKey],\n );\n\n useLayoutEffect(() => {\n const element = ref.current!;\n // The whole browser binding — viewport/DPR reporting, sample normalization,\n // gesture controller — is the shared @embedpdf/web surface, so every\n // framework adapter has one feel. This component keeps only React glue.\n const detachSurface = createStageSurface(element, stage, {\n hub: useHub ? ix : null,\n source: stage.getLensId(),\n zoomGestures,\n });\n // Interaction opt-in lives with the sample source: the same knob that\n // forwards this lens's samples also registers its pan-scroll handler,\n // lens-scoped — two stages on one document can never pan each other.\n const offScroll =\n useHub && ix\n ? ix.registerHandler(createScrollHandler(stage, ix, { panFallback }), {\n source: stage.getLensId(),\n })\n : null;\n\n return () => {\n offScroll?.();\n detachSurface();\n };\n }, [stage, ix, useHub, zoomGestures, panFallback]);\n\n return (\n <div\n ref={ref}\n className={className}\n style={{\n position: 'relative',\n overflow: 'hidden',\n touchAction: 'none',\n ...(useHub ? { cursor: hubCursor } : null),\n ...style,\n }}\n >\n {/* Everything inside binds to this lens by default: a `useStageState()` in\n a page's chrome or a `<SelectionHandles>` in the overlay needs no token. */}\n <StageScope token={token}>\n {pages.map((visiblePage) => (\n <PageSurface\n key={visiblePage.ref.objectNumber} // durable page identity — survives move/delete (matches Angular's `track visiblePage.ref.objectNumber`)\n documentId={docId ?? ''}\n page={visiblePage}\n frame={frame}\n stage={stage}\n render={children}\n chrome={pageChrome}\n />\n ))}\n {/* Anchored UI mounts in the overlay: absolute coords here are the\n projector's overlay space (the stage container). */}\n <ShownPagesProvider value={shownPages}>\n <ProjectorProvider value={projectorBinding}>{overlay}</ProjectorProvider>\n </ShownPagesProvider>\n </StageScope>\n </div>\n );\n}\n\n// ── The hooks ────────────────────────────────────────────────────────────────\n// Every hook takes an optional token; without one it binds to the nearest\n// `<StageScope>` / `<Stage>`, else the main view.\n\n/** The Stage's API (zoom, navigation, reveal, settings), for app chrome. */\nexport function useStage(token?: StageTokenProp): StageCapability {\n return useCapability(useStageToken(token));\n}\n\n/** Subscribe to one stage event for the mounted lifetime: `useStageEvent((stage) => stage.onZoomChanged, handler)`. */\nexport function useStageEvent<T>(\n select: (stage: StageCapability) => EventHook<T>,\n handler: (event: T) => void,\n token?: StageTokenProp,\n): void {\n useCapabilityEvent(useStageToken(token), select, handler);\n}\n\n/** What `useStageState()` returns: the Stage page's State table. */\ntype StageStateValue = (typeof stageState)['empty'];\n\n/**\n * The view's state: the zoom, the current page, the page count, the view\n * rotation and the responsive rules that apply (the page's State table,\n * declared once in `stageState`). Takes a selector, and re-renders only when\n * what it returns changes. Without a document it is `stageState.empty`.\n */\nexport function useStageState<Selected = StageStateValue>(\n select?: (state: StageStateValue) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n // Resolved on every read: a document that closes reads as `empty`, never\n // through its closed capability.\n return useKernelValue((kernel) => {\n const stage = kernel.tryCapability(lens, scoped ?? undefined);\n const state = stage ? stageState.read(stage) : stageState.empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n}\n\n/**\n * The view's settings, or the value `select` picks from them. They belong to\n * the view, so without a document there is none yet and this reads the\n * defaults. Change them with `useStage().updateSettings()`.\n */\nexport function useStageSettings<Selected = StageSettings>(\n select?: (settings: StageSettings) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => {\n const settings = kernel.tryCapability(lens, scoped ?? undefined)?.getSettings();\n const value = settings ?? DEFAULT_SETTINGS;\n return select ? select(value) : (value as unknown as Selected);\n }, shallowEqual);\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as useViewerSettings, E as useSelector, S as useOptionalCapability, a as PageProvider, f as useCapability, g as useDocumentScope, h as useDocumentId, p as useCapabilityEvent, x as useKernelValue } from "./runtime-
|
|
1
|
+
import { D as useViewerSettings, E as useSelector, S as useOptionalCapability, a as PageProvider, f as useCapability, g as useDocumentScope, h as useDocumentId, p as useCapabilityEvent, x as useKernelValue } from "./runtime-tD5iJU83.js";
|
|
2
2
|
import { ProjectorProvider, ShownPagesProvider } from "./anchored.js";
|
|
3
3
|
import { n as useStageToken, t as StageScope } from "./stage-scope-D7ZeNduT.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -299,4 +299,4 @@ function useStageSettings(select, token) {
|
|
|
299
299
|
//#endregion
|
|
300
300
|
export { useStageState as a, useStageSettings as i, useStage as n, Scrollbar as o, useStageEvent as r, useScrollMetrics as s, Stage as t };
|
|
301
301
|
|
|
302
|
-
//# sourceMappingURL=stage-
|
|
302
|
+
//# sourceMappingURL=stage-BqfBjDu6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage-DoVekb83.js","names":["InteractionToken","InteractionPublicToken"],"sources":["../src/scrollbar.tsx","../src/stage.tsx"],"sourcesContent":["/**\n * Headless scrollbar for a Stage lens — a pure view of `stage.getScrollMetrics()`.\n *\n * The Stage exposes the native scroller contract (scrollTop/scrollHeight/\n * clientHeight, in screen px — see plugin-stage's README); this component turns\n * it into a native-feeling bar: thumb drag with pointer capture and a preserved\n * grab point, track paging with press-and-hold repeat that stops at the\n * pointer, a minimum thumb length on long documents, and macOS-style overlay\n * auto-hide. On an unbounded stage the metrics already carry the Figma\n * semantics (the range is the union of content and view), so the same bar\n * shrinks toward the edge as you pan away and rides you back — the mapping is\n * frozen for the duration of a thumb drag so the thumb never chases itself\n * while the union re-collapses.\n *\n * Headless styling contract: geometry and behavior live here; looks live in\n * your CSS. Style via `className`/`thumbClassName` (or the style props) against\n * [data-embedpdf-scrollbar][data-axis=\"y\"][data-state=\"visible|hidden\"]\n * [data-embedpdf-scrollbar][data-dragging]\n * [data-embedpdf-scrollbar-thumb]\n * The defaults are deliberately minimal: a transparent track pinned to the\n * viewport edge and a `var(--epdf-scrollbar-thumb, …)` pill, so the bar works\n * unstyled and disappears into any theme. Build something else entirely\n * (minimap, progress ring) from `useScrollMetrics()` + `stage.scrollTo()`.\n */\n\nimport * as React from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { StageCapability } from '@embedpdf/plugin-stage/contract';\nimport type { ScrollMetrics, StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { createScrollbarPresses, paintDefault, scrollbarLayout } from '@embedpdf/web';\nimport type { ScrollbarTrack } from '@embedpdf/web';\nimport { useCapability, useDocumentScope, useKernelValue } from './runtime';\nimport { useStageToken } from './stage-scope';\n\nexport type ScrollbarAxis = 'x' | 'y';\n\n/** What a view with nothing to scroll measures: no document, or not placed yet. */\nconst NO_SCROLL: ScrollMetrics = Object.freeze({\n scrollLeft: 0,\n scrollTop: 0,\n scrollWidth: 0,\n scrollHeight: 0,\n clientWidth: 0,\n clientHeight: 0,\n scrollableX: false,\n scrollableY: false,\n});\n\n/**\n * The view's scroll position as a scrolling element has it: `scrollTop`,\n * `scrollHeight`, `clientHeight` and their horizontal twins, in screen pixels.\n * The raw material for scroll UI of your own; all zero without a document.\n */\nexport function useScrollMetrics(token?: CapabilityToken<StageCapability>): ScrollMetrics {\n const lens = asHost(useStageToken(token));\n const scoped = useDocumentScope();\n // The capability keeps the same metrics object until a number moves.\n return useKernelValue(\n (kernel) => kernel.tryCapability(lens, scoped ?? undefined)?.getScrollMetrics() ?? NO_SCROLL,\n );\n}\n\n// Scroll metrics live on the host lens (the same runtime token, typed wider):\n// a scrollbar is chrome that drives the camera, not a document-level consumer.\nconst asHost = (token: CapabilityToken<StageCapability>) =>\n token as unknown as CapabilityToken<StageHostCapability>;\n\nexport interface ScrollbarProps {\n axis: ScrollbarAxis;\n /** The stage lens to scroll (default: the nearest `<StageScope>` / `<Stage>`, else the main StageToken). */\n token?: CapabilityToken<StageCapability>;\n /**\n * Overlay auto-hide: fade `autoHide` ms after the camera stops moving\n * (drags and hover pin it visible; a hidden bar ignores the pointer, like\n * macOS). `false` = always visible. Visibility is published as\n * `data-state=\"visible|hidden\"` — the default style fades opacity; replace\n * it in CSS for any other treatment. Default 1200.\n */\n autoHide?: number | false;\n /** Minimum thumb length in px — native bars never vanish on long documents.\n * Position stays proportional via the standard travel remap. Default 24. */\n minThumbSize?: number;\n /**\n * Pressing the track: 'page' steps by 90% of a viewport toward the pointer,\n * repeats while held, and stops when the thumb reaches the pointer (the\n * native default); 'jump' centers the thumb at the pointer and hands off to\n * a drag (the macOS option-click / Figma feel). Default 'page'.\n */\n trackPress?: 'page' | 'jump';\n /** Track class/style. The default style pins the bar to the right (y) or\n * bottom (x) edge of the nearest positioned ancestor — the Stage container\n * when rendered in its `overlay`; override to place it anywhere else. */\n className?: string;\n style?: React.CSSProperties;\n thumbClassName?: string;\n thumbStyle?: React.CSSProperties;\n}\n\nexport function Scrollbar({\n axis,\n token: explicitToken,\n autoHide = 1200,\n minThumbSize = 24,\n trackPress = 'page',\n className,\n style,\n thumbClassName,\n thumbStyle,\n}: ScrollbarProps) {\n const token = useStageToken(explicitToken);\n const stage = useCapability(asHost(token));\n const metrics = useScrollMetrics(token);\n const vertical = axis === 'y';\n const scrollable = vertical ? metrics.scrollableY : metrics.scrollableX;\n\n const trackRef = useRef<HTMLDivElement>(null);\n const [trackPx, setTrackPx] = useState(0);\n const [dragging, setDragging] = useState(false);\n const [hovered, setHovered] = useState(false);\n const [active, setActive] = useState(true); // camera moved recently\n\n // The thumb along the track: one formula for drawing, dragging and paging, shared with every framework.\n const layout = scrollbarLayout(metrics, vertical, trackPx, minThumbSize);\n\n // ── overlay auto-hide: any metrics change re-arms the fade timer ──────────\n const hideAfter = autoHide === false ? 0 : autoHide;\n useEffect(() => {\n if (!hideAfter) return;\n setActive(true);\n const timer = setTimeout(() => setActive(false), hideAfter);\n return () => clearTimeout(timer);\n }, [metrics, hideAfter]);\n const shown = !hideAfter || active || hovered || dragging;\n\n // ── track measurement (px mapping needs real geometry) ────────────────────\n useEffect(() => {\n const element = trackRef.current;\n if (!element) return;\n const measure = () => setTrackPx(vertical ? element.clientHeight : element.clientWidth);\n const ro = new ResizeObserver(measure);\n ro.observe(element);\n measure();\n return () => ro.disconnect();\n }, [vertical, scrollable]);\n\n // ── interactions: `@embedpdf/web`'s press handling (thumb drags, track paging and jumps),\n // one per bar, handed the bar as this render has it on every event. ──\n const presses = useMemo(() => createScrollbarPresses(setDragging), []);\n const track = (): ScrollbarTrack => ({\n stage,\n vertical,\n element: trackRef.current!,\n layout,\n // Fresh geometry for paging steps — read from the capability, never a stale render.\n liveLayout: () => scrollbarLayout(stage.getScrollMetrics(), vertical, trackPx, minThumbSize),\n });\n\n useEffect(() => () => presses.release(), [presses]); // unmount: no orphaned repeat timers\n\n if (!scrollable) return null;\n\n const trackDefaults: React.CSSProperties = vertical\n ? { position: 'absolute', top: 0, right: 0, bottom: 0, width: 12 }\n : { position: 'absolute', left: 0, right: 0, bottom: 0, height: 12 };\n const thumbDefaults: React.CSSProperties = vertical\n ? {\n position: 'absolute',\n left: 2,\n right: 2,\n top: layout.thumbPosition,\n height: layout.thumbLength,\n }\n : {\n position: 'absolute',\n top: 2,\n bottom: 2,\n left: layout.thumbPosition,\n width: layout.thumbLength,\n };\n\n return (\n <div\n ref={trackRef}\n role=\"scrollbar\"\n aria-orientation={vertical ? 'vertical' : 'horizontal'}\n aria-valuemin={0}\n aria-valuemax={Math.round(layout.maxOffset)}\n aria-valuenow={Math.round(layout.offset)}\n data-embedpdf-scrollbar=\"\"\n data-axis={axis}\n data-state={shown ? 'visible' : 'hidden'}\n data-dragging={dragging ? '' : undefined}\n className={className}\n style={{\n ...trackDefaults,\n touchAction: 'none',\n userSelect: 'none',\n opacity: shown ? 1 : 0,\n transition: 'opacity 200ms',\n // a hidden overlay bar must not eat the clicks under it\n pointerEvents: shown ? 'auto' : 'none',\n ...style,\n }}\n // Pointer capture retargets to the pressed element and bubbles here — one\n // move/up pair serves thumb drags, jump-drags, and paging alike.\n onPointerDown={(event) => presses.pressTrack(event, track(), trackPress)}\n onPointerMove={(event) => presses.move(event, track())}\n onPointerUp={() => presses.release()}\n onPointerCancel={() => presses.release()}\n onPointerEnter={() => setHovered(true)}\n onPointerLeave={() => setHovered(false)}\n >\n <div\n data-embedpdf-scrollbar-thumb=\"\"\n className={thumbClassName}\n style={{\n ...thumbDefaults,\n borderRadius: 6,\n background: paintDefault('scrollbar-thumb'),\n ...thumbStyle,\n }}\n onPointerDown={(event) => presses.pressThumb(event, track())}\n />\n </div>\n );\n}\n","/**\n * The Stage and its hooks.\n *\n * <Stage> virtualizes and positions page surfaces by the camera, and hands each\n * one to your render prop — you bring the layers. (The standalone, Stage-free\n * single-page surface lives in `./page-view` so it never pulls the stage plugin.)\n * The hooks are the plugin's four: `useStage()` (the API), `useStageState()`,\n * `useStageEvent()` and `useStageSettings()`; `<Scrollbar>` is the Stage's too.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-stage';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport { createScrollHandler, DEFAULT_SETTINGS, stageState } from '@embedpdf/plugin-stage';\nimport type { StageCapability, StageSettings, VisiblePage } from '@embedpdf/plugin-stage';\nimport type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport { shallowEqual, toPageRef } from '@embedpdf/core';\nimport type { CapabilityToken, EventHook } from '@embedpdf/core';\nimport type { PageFrame } from '@embedpdf/core-geometry';\nimport { InteractionToken as InteractionPublicToken } from '@embedpdf/plugin-interaction/contract';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';\nimport {\n createStageSurface,\n makePageContext,\n pageSurfaceLayout,\n paint,\n stagePageDemand,\n stageViewProjector,\n} from '@embedpdf/web';\nimport {\n ProjectorProvider,\n ShownPagesProvider,\n type ProjectorBinding,\n type ShownPages,\n type ViewProjector,\n} from './anchored';\nimport {\n PageProvider,\n useCapability,\n useCapabilityEvent,\n useDocumentId,\n useDocumentScope,\n useKernelValue,\n useOptionalCapability,\n useSelector,\n useViewerSettings,\n} from './runtime';\nimport type { PageContextValue } from './runtime';\nimport { StageScope, useStageToken } from './stage-scope';\nimport type { StageTokenProp } from './stage-scope';\n\nexport { StageScope, useStageToken } from './stage-scope';\nexport type { StageScopeProps, StageTokenProp } from './stage-scope';\nexport { Scrollbar, useScrollMetrics } from './scrollbar';\nexport type { ScrollbarAxis, ScrollbarProps } from './scrollbar';\n\nfunction PageSurface({\n documentId,\n page,\n frame,\n stage,\n render,\n chrome,\n}: {\n documentId: string;\n page: VisiblePage;\n /** Reserved chrome bands around the page (screen px); the layout reserved the\n * matching space, so the outer box tiles into it. */\n frame: PageFrame;\n /** The stage capability — the demand getter reads visibility live off it. */\n stage: StageHostCapability;\n render: (page: PageContextValue) => React.ReactNode;\n chrome?: (page: PageContextValue) => React.ReactNode;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n const transform = page.transform;\n // The viewer's `page` settings; `--epdf-page-*` CSS variables win over them.\n const look = useViewerSettings((settings) => settings.page);\n // The outer, shadow and content boxes, all from the transform; `screenX`/`screenY` are the\n // device-snapped footprint's top-left.\n const layout = pageSurfaceLayout(transform, frame, { x: page.screenX, y: page.screenY });\n // The page address this surface hands its layers. The stage rebuilds\n // `VisiblePage.ref` every camera frame, so it is memoized by the number:\n // identity-stable per page, safe for layers to key effects on.\n const pageObjectNumber = page.ref.objectNumber;\n const pageRef = useMemo(() => toPageRef(pageObjectNumber), [pageObjectNumber]);\n // The page-view demand is a pull: the getter closes over stable references\n // (capability + page address) and reads the stage's live state at call time.\n // Absent from the visible set = zero rect (\"want nothing\"), distinct from\n // PageView's undefined getter (\"whole page\").\n const ctx = useMemo(\n () =>\n makePageContext(\n documentId,\n // the hosting lens — per-view raster planning keys tile state by it,\n // so a thumbnail rail and the main view never fight over one plan\n stage.getLensId(),\n pageRef,\n page.pageIndex,\n frame,\n transform,\n () => ref.current!.getBoundingClientRect(),\n () => stagePageDemand(stage, pageObjectNumber, transform.deviceWidth),\n ),\n [documentId, pageRef, pageObjectNumber, page.pageIndex, frame, transform, stage],\n );\n return (\n <div style={{ position: 'absolute', ...layout.outer }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — axis-aligned at the content box (inset by the frame),\n transparent fill so it can never peek out behind the bitmap, and it\n stays put under rotation. */}\n <div\n style={{\n position: 'absolute',\n ...layout.shadow,\n boxShadow: paint('page-shadow', look.shadow),\n }}\n />\n {/* the page: white backing + bitmap as ONE rasterized box, so there is no\n seam between them and nothing white larger than the bitmap to leak.\n The ONLY thing rotation turns; markers/annotations ride it in content\n coordinates. Rotation 0 carries no transform → pixel-snaps cleanly. */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n ...layout.content,\n background: paint('page-background', look.background),\n transform: layout.turn ?? undefined,\n // We render our own selection highlights — suppress native text/image\n // selection (and the double-click image grab) on the whole page subtree.\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {render(ctx)}\n </div>\n {/* box-space chrome — labels, selection border, per-page buttons — fills\n the outer box, NEVER rotates. Bands are plain regions: a label is\n `bottom:0; height: frame.bottom`, a button row `top:0; height: frame.top`. */}\n {chrome?.(ctx)}\n </PageProvider>\n </div>\n );\n}\n\nexport interface StageProps {\n /**\n * Page-space content for each visible page (RenderLayer, annotations,\n * markers). Rendered inside the page's content frame, so it rotates with the\n * page's display rotation — coordinates are plain PDF points.\n */\n children: (page: PageContextValue) => React.ReactNode;\n /**\n * Box-space chrome for each visible page (page-number label, selection\n * border, a per-page rotate/delete button). Rendered into the outer box\n * (content + reserved `pageFrame`), so it does not rotate and the reserved\n * bands are plain regions (`bottom:0; height: page.frame.bottom`). The three\n * coordinate spaces: `children` (page content), `pageChrome` (page box +\n * frame), `overlay` (viewport).\n */\n pageChrome?: (page: PageContextValue) => React.ReactNode;\n /** Viewport-space UI (menus, controls) rendered above the pages. */\n overlay?: React.ReactNode;\n /**\n * The controlled form of the active tool: while set, the interaction hub's\n * active tool follows this value (re-applied when the document changes),\n * and `onToolChange` reports every change so the owner can update it. Omit\n * both for the uncontrolled default (`useInteraction().activateTool`).\n */\n tool?: string;\n /** Fires on every tool change of this stage's document (controlled or not). */\n onToolChange?: (toolId: string) => void;\n /** The stage lens to drive (default: the nearest `<StageScope>`, else the main StageToken). */\n token?: StageTokenProp;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport function Stage({\n children,\n pageChrome,\n overlay,\n tool,\n onToolChange,\n token: explicitToken,\n className,\n style,\n}: StageProps) {\n const token = useStageToken(explicitToken);\n // The surface is a host of the lens: it reports viewport size, drives gestures and\n // reads the lens id. The host contract is the same runtime token, typed wider.\n const stage = useCapability(token as unknown as CapabilityToken<StageHostCapability>);\n const ix = useOptionalCapability(InteractionToken);\n // Controlled tool: the prop is the source of truth whenever it is set.\n useEffect(() => {\n if (tool === undefined || !ix) return;\n if (ix.getActiveToolId() !== tool) ix.activateTool(tool);\n }, [tool, ix]);\n useCapabilityEvent(\n InteractionPublicToken,\n (interaction) => interaction.onToolChanged,\n (event) => onToolChange?.(event.toolId),\n );\n // How this view takes pointer input is its own settings (`interaction`,\n // `panFallback`, `zoomGestures`): a change rebinds the surface below.\n const interaction = useSelector(token, (stage) => stage.getSettings().interaction);\n const panFallback = useSelector(token, (stage) => stage.getSettings().panFallback);\n const zoomGestures = useSelector(token, (stage) => stage.getSettings().zoomGestures);\n // Routing pointer input to the interaction hub (page-resolved) also\n // registers this view's tool-gated pan-scroll handler with it, scoped to\n // the view, so two Stages on one document never pan each other. Without\n // the hub the Stage falls back to its own drag-to-pan.\n const useHub = interaction && !!ix;\n // The hub's resolved cursor (text/grab/…), applied to the viewport when driving.\n const hubCursor = useKernelValue(() => ix?.getCursor() ?? 'default');\n const ref = useRef<HTMLDivElement>(null);\n const docId = useDocumentId();\n // visiblePages already folds in the camera (each page carries its device-snapped\n // screenX/screenY + transform), so panning re-emits the list — no separate\n // camera subscription needed for positioning.\n const pages = useSelector(token, (stage) => stage.listVisiblePages()); // memoized -> stable ref\n // Reserved chrome bands (screen px), uniform across pages — the frame the\n // outer box reserves and `pageChrome` paints into.\n const frame = useSelector(\n token,\n (stage) => stage.getSettings().pageFrame,\n (left, right) =>\n left.top === right.top &&\n left.right === right.right &&\n left.bottom === right.bottom &&\n left.left === right.left,\n );\n\n // The Stage's ViewProjector: anchored UI (menus, popovers) positions through\n // the camera — pure state, no DOM reads, no portal, and no subscription:\n // `pages` (visiblePages) is the binding's revision, so a camera change\n // re-renders the pages and every anchored consumer in the same React\n // commit — surface and overlay can never paint a frame apart.\n const projector = useMemo<ViewProjector>(() => stageViewProjector(() => stage), [stage]);\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: pages }),\n [projector, pages],\n );\n // The pages on screen as one value that changes only when a page comes or\n // goes, so anchored UI on the other pages sits out every camera frame.\n const shownKey = pages.map((page) => page.ref.objectNumber).join(',');\n const shownPages = useMemo<ShownPages>(\n () => new Set(shownKey ? shownKey.split(',').map(Number) : []),\n [shownKey],\n );\n\n useLayoutEffect(() => {\n const element = ref.current!;\n // The whole browser binding — viewport/DPR reporting, sample normalization,\n // gesture controller — is the shared @embedpdf/web surface, so every\n // framework adapter has one feel. This component keeps only React glue.\n const detachSurface = createStageSurface(element, stage, {\n hub: useHub ? ix : null,\n source: stage.getLensId(),\n zoomGestures,\n });\n // Interaction opt-in lives with the sample source: the same knob that\n // forwards this lens's samples also registers its pan-scroll handler,\n // lens-scoped — two stages on one document can never pan each other.\n const offScroll =\n useHub && ix\n ? ix.registerHandler(createScrollHandler(stage, ix, { panFallback }), {\n source: stage.getLensId(),\n })\n : null;\n\n return () => {\n offScroll?.();\n detachSurface();\n };\n }, [stage, ix, useHub, zoomGestures, panFallback]);\n\n return (\n <div\n ref={ref}\n className={className}\n style={{\n position: 'relative',\n overflow: 'hidden',\n touchAction: 'none',\n ...(useHub ? { cursor: hubCursor } : null),\n ...style,\n }}\n >\n {/* Everything inside binds to this lens by default: a `useStageState()` in\n a page's chrome or a `<SelectionHandles>` in the overlay needs no token. */}\n <StageScope token={token}>\n {pages.map((visiblePage) => (\n <PageSurface\n key={visiblePage.ref.objectNumber} // durable page identity — survives move/delete (matches Angular's `track visiblePage.ref.objectNumber`)\n documentId={docId ?? ''}\n page={visiblePage}\n frame={frame}\n stage={stage}\n render={children}\n chrome={pageChrome}\n />\n ))}\n {/* Anchored UI mounts in the overlay: absolute coords here are the\n projector's overlay space (the stage container). */}\n <ShownPagesProvider value={shownPages}>\n <ProjectorProvider value={projectorBinding}>{overlay}</ProjectorProvider>\n </ShownPagesProvider>\n </StageScope>\n </div>\n );\n}\n\n// ── The hooks ────────────────────────────────────────────────────────────────\n// Every hook takes an optional token; without one it binds to the nearest\n// `<StageScope>` / `<Stage>`, else the main view.\n\n/** The Stage's API (zoom, navigation, reveal, settings), for app chrome. */\nexport function useStage(token?: StageTokenProp): StageCapability {\n return useCapability(useStageToken(token));\n}\n\n/** Subscribe to one stage event for the mounted lifetime: `useStageEvent((stage) => stage.onZoomChanged, handler)`. */\nexport function useStageEvent<T>(\n select: (stage: StageCapability) => EventHook<T>,\n handler: (event: T) => void,\n token?: StageTokenProp,\n): void {\n useCapabilityEvent(useStageToken(token), select, handler);\n}\n\n/** What `useStageState()` returns: the Stage page's State table. */\ntype StageStateValue = (typeof stageState)['empty'];\n\n/**\n * The view's state: the zoom, the current page, the page count, the view\n * rotation and the responsive rules that apply (the page's State table,\n * declared once in `stageState`). Takes a selector, and re-renders only when\n * what it returns changes. Without a document it is `stageState.empty`.\n */\nexport function useStageState<Selected = StageStateValue>(\n select?: (state: StageStateValue) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n // Resolved on every read: a document that closes reads as `empty`, never\n // through its closed capability.\n return useKernelValue((kernel) => {\n const stage = kernel.tryCapability(lens, scoped ?? undefined);\n const state = stage ? stageState.read(stage) : stageState.empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n}\n\n/**\n * The view's settings, or the value `select` picks from them. They belong to\n * the view, so without a document there is none yet and this reads the\n * defaults. Change them with `useStage().updateSettings()`.\n */\nexport function useStageSettings<Selected = StageSettings>(\n select?: (settings: StageSettings) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => {\n const settings = kernel.tryCapability(lens, scoped ?? undefined)?.getSettings();\n const value = settings ?? DEFAULT_SETTINGS;\n return select ? select(value) : (value as unknown as Selected);\n }, shallowEqual);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stage-BqfBjDu6.js","names":["InteractionToken","InteractionPublicToken"],"sources":["../src/scrollbar.tsx","../src/stage.tsx"],"sourcesContent":["/**\n * Headless scrollbar for a Stage lens — a pure view of `stage.getScrollMetrics()`.\n *\n * The Stage exposes the native scroller contract (scrollTop/scrollHeight/\n * clientHeight, in screen px — see plugin-stage's README); this component turns\n * it into a native-feeling bar: thumb drag with pointer capture and a preserved\n * grab point, track paging with press-and-hold repeat that stops at the\n * pointer, a minimum thumb length on long documents, and macOS-style overlay\n * auto-hide. On an unbounded stage the metrics already carry the Figma\n * semantics (the range is the union of content and view), so the same bar\n * shrinks toward the edge as you pan away and rides you back — the mapping is\n * frozen for the duration of a thumb drag so the thumb never chases itself\n * while the union re-collapses.\n *\n * Headless styling contract: geometry and behavior live here; looks live in\n * your CSS. Style via `className`/`thumbClassName` (or the style props) against\n * [data-embedpdf-scrollbar][data-axis=\"y\"][data-state=\"visible|hidden\"]\n * [data-embedpdf-scrollbar][data-dragging]\n * [data-embedpdf-scrollbar-thumb]\n * The defaults are deliberately minimal: a transparent track pinned to the\n * viewport edge and a `var(--epdf-scrollbar-thumb, …)` pill, so the bar works\n * unstyled and disappears into any theme. Build something else entirely\n * (minimap, progress ring) from `useScrollMetrics()` + `stage.scrollTo()`.\n */\n\nimport * as React from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { StageCapability } from '@embedpdf/plugin-stage/contract';\nimport type { ScrollMetrics, StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { createScrollbarPresses, paintDefault, scrollbarLayout } from '@embedpdf/web';\nimport type { ScrollbarTrack } from '@embedpdf/web';\nimport { useCapability, useDocumentScope, useKernelValue } from './runtime';\nimport { useStageToken } from './stage-scope';\n\nexport type ScrollbarAxis = 'x' | 'y';\n\n/** What a view with nothing to scroll measures: no document, or not placed yet. */\nconst NO_SCROLL: ScrollMetrics = Object.freeze({\n scrollLeft: 0,\n scrollTop: 0,\n scrollWidth: 0,\n scrollHeight: 0,\n clientWidth: 0,\n clientHeight: 0,\n scrollableX: false,\n scrollableY: false,\n});\n\n/**\n * The view's scroll position as a scrolling element has it: `scrollTop`,\n * `scrollHeight`, `clientHeight` and their horizontal twins, in screen pixels.\n * The raw material for scroll UI of your own; all zero without a document.\n */\nexport function useScrollMetrics(token?: CapabilityToken<StageCapability>): ScrollMetrics {\n const lens = asHost(useStageToken(token));\n const scoped = useDocumentScope();\n // The capability keeps the same metrics object until a number moves.\n return useKernelValue(\n (kernel) => kernel.tryCapability(lens, scoped ?? undefined)?.getScrollMetrics() ?? NO_SCROLL,\n );\n}\n\n// Scroll metrics live on the host lens (the same runtime token, typed wider):\n// a scrollbar is chrome that drives the camera, not a document-level consumer.\nconst asHost = (token: CapabilityToken<StageCapability>) =>\n token as unknown as CapabilityToken<StageHostCapability>;\n\nexport interface ScrollbarProps {\n axis: ScrollbarAxis;\n /** The stage lens to scroll (default: the nearest `<StageScope>` / `<Stage>`, else the main StageToken). */\n token?: CapabilityToken<StageCapability>;\n /**\n * Overlay auto-hide: fade `autoHide` ms after the camera stops moving\n * (drags and hover pin it visible; a hidden bar ignores the pointer, like\n * macOS). `false` = always visible. Visibility is published as\n * `data-state=\"visible|hidden\"` — the default style fades opacity; replace\n * it in CSS for any other treatment. Default 1200.\n */\n autoHide?: number | false;\n /** Minimum thumb length in px — native bars never vanish on long documents.\n * Position stays proportional via the standard travel remap. Default 24. */\n minThumbSize?: number;\n /**\n * Pressing the track: 'page' steps by 90% of a viewport toward the pointer,\n * repeats while held, and stops when the thumb reaches the pointer (the\n * native default); 'jump' centers the thumb at the pointer and hands off to\n * a drag (the macOS option-click / Figma feel). Default 'page'.\n */\n trackPress?: 'page' | 'jump';\n /** Track class/style. The default style pins the bar to the right (y) or\n * bottom (x) edge of the nearest positioned ancestor — the Stage container\n * when rendered in its `overlay`; override to place it anywhere else. */\n className?: string;\n style?: React.CSSProperties;\n thumbClassName?: string;\n thumbStyle?: React.CSSProperties;\n}\n\nexport function Scrollbar({\n axis,\n token: explicitToken,\n autoHide = 1200,\n minThumbSize = 24,\n trackPress = 'page',\n className,\n style,\n thumbClassName,\n thumbStyle,\n}: ScrollbarProps) {\n const token = useStageToken(explicitToken);\n const stage = useCapability(asHost(token));\n const metrics = useScrollMetrics(token);\n const vertical = axis === 'y';\n const scrollable = vertical ? metrics.scrollableY : metrics.scrollableX;\n\n const trackRef = useRef<HTMLDivElement>(null);\n const [trackPx, setTrackPx] = useState(0);\n const [dragging, setDragging] = useState(false);\n const [hovered, setHovered] = useState(false);\n const [active, setActive] = useState(true); // camera moved recently\n\n // The thumb along the track: one formula for drawing, dragging and paging, shared with every framework.\n const layout = scrollbarLayout(metrics, vertical, trackPx, minThumbSize);\n\n // ── overlay auto-hide: any metrics change re-arms the fade timer ──────────\n const hideAfter = autoHide === false ? 0 : autoHide;\n useEffect(() => {\n if (!hideAfter) return;\n setActive(true);\n const timer = setTimeout(() => setActive(false), hideAfter);\n return () => clearTimeout(timer);\n }, [metrics, hideAfter]);\n const shown = !hideAfter || active || hovered || dragging;\n\n // ── track measurement (px mapping needs real geometry) ────────────────────\n useEffect(() => {\n const element = trackRef.current;\n if (!element) return;\n const measure = () => setTrackPx(vertical ? element.clientHeight : element.clientWidth);\n const ro = new ResizeObserver(measure);\n ro.observe(element);\n measure();\n return () => ro.disconnect();\n }, [vertical, scrollable]);\n\n // ── interactions: `@embedpdf/web`'s press handling (thumb drags, track paging and jumps),\n // one per bar, handed the bar as this render has it on every event. ──\n const presses = useMemo(() => createScrollbarPresses(setDragging), []);\n const track = (): ScrollbarTrack => ({\n stage,\n vertical,\n element: trackRef.current!,\n layout,\n // Fresh geometry for paging steps — read from the capability, never a stale render.\n liveLayout: () => scrollbarLayout(stage.getScrollMetrics(), vertical, trackPx, minThumbSize),\n });\n\n useEffect(() => () => presses.release(), [presses]); // unmount: no orphaned repeat timers\n\n if (!scrollable) return null;\n\n const trackDefaults: React.CSSProperties = vertical\n ? { position: 'absolute', top: 0, right: 0, bottom: 0, width: 12 }\n : { position: 'absolute', left: 0, right: 0, bottom: 0, height: 12 };\n const thumbDefaults: React.CSSProperties = vertical\n ? {\n position: 'absolute',\n left: 2,\n right: 2,\n top: layout.thumbPosition,\n height: layout.thumbLength,\n }\n : {\n position: 'absolute',\n top: 2,\n bottom: 2,\n left: layout.thumbPosition,\n width: layout.thumbLength,\n };\n\n return (\n <div\n ref={trackRef}\n role=\"scrollbar\"\n aria-orientation={vertical ? 'vertical' : 'horizontal'}\n aria-valuemin={0}\n aria-valuemax={Math.round(layout.maxOffset)}\n aria-valuenow={Math.round(layout.offset)}\n data-embedpdf-scrollbar=\"\"\n data-axis={axis}\n data-state={shown ? 'visible' : 'hidden'}\n data-dragging={dragging ? '' : undefined}\n className={className}\n style={{\n ...trackDefaults,\n touchAction: 'none',\n userSelect: 'none',\n opacity: shown ? 1 : 0,\n transition: 'opacity 200ms',\n // a hidden overlay bar must not eat the clicks under it\n pointerEvents: shown ? 'auto' : 'none',\n ...style,\n }}\n // Pointer capture retargets to the pressed element and bubbles here — one\n // move/up pair serves thumb drags, jump-drags, and paging alike.\n onPointerDown={(event) => presses.pressTrack(event, track(), trackPress)}\n onPointerMove={(event) => presses.move(event, track())}\n onPointerUp={() => presses.release()}\n onPointerCancel={() => presses.release()}\n onPointerEnter={() => setHovered(true)}\n onPointerLeave={() => setHovered(false)}\n >\n <div\n data-embedpdf-scrollbar-thumb=\"\"\n className={thumbClassName}\n style={{\n ...thumbDefaults,\n borderRadius: 6,\n background: paintDefault('scrollbar-thumb'),\n ...thumbStyle,\n }}\n onPointerDown={(event) => presses.pressThumb(event, track())}\n />\n </div>\n );\n}\n","/**\n * The Stage and its hooks.\n *\n * <Stage> virtualizes and positions page surfaces by the camera, and hands each\n * one to your render prop — you bring the layers. (The standalone, Stage-free\n * single-page surface lives in `./page-view` so it never pulls the stage plugin.)\n * The hooks are the plugin's four: `useStage()` (the API), `useStageState()`,\n * `useStageEvent()` and `useStageSettings()`; `<Scrollbar>` is the Stage's too.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-stage';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport { createScrollHandler, DEFAULT_SETTINGS, stageState } from '@embedpdf/plugin-stage';\nimport type { StageCapability, StageSettings, VisiblePage } from '@embedpdf/plugin-stage';\nimport type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';\nimport { shallowEqual, toPageRef } from '@embedpdf/core';\nimport type { CapabilityToken, EventHook } from '@embedpdf/core';\nimport type { PageFrame } from '@embedpdf/core-geometry';\nimport { InteractionToken as InteractionPublicToken } from '@embedpdf/plugin-interaction/contract';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';\nimport {\n createStageSurface,\n makePageContext,\n pageSurfaceLayout,\n paint,\n stagePageDemand,\n stageViewProjector,\n} from '@embedpdf/web';\nimport {\n ProjectorProvider,\n ShownPagesProvider,\n type ProjectorBinding,\n type ShownPages,\n type ViewProjector,\n} from './anchored';\nimport {\n PageProvider,\n useCapability,\n useCapabilityEvent,\n useDocumentId,\n useDocumentScope,\n useKernelValue,\n useOptionalCapability,\n useSelector,\n useViewerSettings,\n} from './runtime';\nimport type { PageContextValue } from './runtime';\nimport { StageScope, useStageToken } from './stage-scope';\nimport type { StageTokenProp } from './stage-scope';\n\nexport { StageScope, useStageToken } from './stage-scope';\nexport type { StageScopeProps, StageTokenProp } from './stage-scope';\nexport { Scrollbar, useScrollMetrics } from './scrollbar';\nexport type { ScrollbarAxis, ScrollbarProps } from './scrollbar';\n\nfunction PageSurface({\n documentId,\n page,\n frame,\n stage,\n render,\n chrome,\n}: {\n documentId: string;\n page: VisiblePage;\n /** Reserved chrome bands around the page (screen px); the layout reserved the\n * matching space, so the outer box tiles into it. */\n frame: PageFrame;\n /** The stage capability — the demand getter reads visibility live off it. */\n stage: StageHostCapability;\n render: (page: PageContextValue) => React.ReactNode;\n chrome?: (page: PageContextValue) => React.ReactNode;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n const transform = page.transform;\n // The viewer's `page` settings; `--epdf-page-*` CSS variables win over them.\n const look = useViewerSettings((settings) => settings.page);\n // The outer, shadow and content boxes, all from the transform; `screenX`/`screenY` are the\n // device-snapped footprint's top-left.\n const layout = pageSurfaceLayout(transform, frame, { x: page.screenX, y: page.screenY });\n // The page address this surface hands its layers. The stage rebuilds\n // `VisiblePage.ref` every camera frame, so it is memoized by the number:\n // identity-stable per page, safe for layers to key effects on.\n const pageObjectNumber = page.ref.objectNumber;\n const pageRef = useMemo(() => toPageRef(pageObjectNumber), [pageObjectNumber]);\n // The page-view demand is a pull: the getter closes over stable references\n // (capability + page address) and reads the stage's live state at call time.\n // Absent from the visible set = zero rect (\"want nothing\"), distinct from\n // PageView's undefined getter (\"whole page\").\n const ctx = useMemo(\n () =>\n makePageContext(\n documentId,\n // the hosting lens — per-view raster planning keys tile state by it,\n // so a thumbnail rail and the main view never fight over one plan\n stage.getLensId(),\n pageRef,\n page.pageIndex,\n frame,\n transform,\n () => ref.current!.getBoundingClientRect(),\n () => stagePageDemand(stage, pageObjectNumber, transform.deviceWidth),\n ),\n [documentId, pageRef, pageObjectNumber, page.pageIndex, frame, transform, stage],\n );\n return (\n <div style={{ position: 'absolute', ...layout.outer }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — axis-aligned at the content box (inset by the frame),\n transparent fill so it can never peek out behind the bitmap, and it\n stays put under rotation. */}\n <div\n style={{\n position: 'absolute',\n ...layout.shadow,\n boxShadow: paint('page-shadow', look.shadow),\n }}\n />\n {/* the page: white backing + bitmap as ONE rasterized box, so there is no\n seam between them and nothing white larger than the bitmap to leak.\n The ONLY thing rotation turns; markers/annotations ride it in content\n coordinates. Rotation 0 carries no transform → pixel-snaps cleanly. */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n ...layout.content,\n background: paint('page-background', look.background),\n transform: layout.turn ?? undefined,\n // We render our own selection highlights — suppress native text/image\n // selection (and the double-click image grab) on the whole page subtree.\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {render(ctx)}\n </div>\n {/* box-space chrome — labels, selection border, per-page buttons — fills\n the outer box, NEVER rotates. Bands are plain regions: a label is\n `bottom:0; height: frame.bottom`, a button row `top:0; height: frame.top`. */}\n {chrome?.(ctx)}\n </PageProvider>\n </div>\n );\n}\n\nexport interface StageProps {\n /**\n * Page-space content for each visible page (RenderLayer, annotations,\n * markers). Rendered inside the page's content frame, so it rotates with the\n * page's display rotation — coordinates are plain PDF points.\n */\n children: (page: PageContextValue) => React.ReactNode;\n /**\n * Box-space chrome for each visible page (page-number label, selection\n * border, a per-page rotate/delete button). Rendered into the outer box\n * (content + reserved `pageFrame`), so it does not rotate and the reserved\n * bands are plain regions (`bottom:0; height: page.frame.bottom`). The three\n * coordinate spaces: `children` (page content), `pageChrome` (page box +\n * frame), `overlay` (viewport).\n */\n pageChrome?: (page: PageContextValue) => React.ReactNode;\n /** Viewport-space UI (menus, controls) rendered above the pages. */\n overlay?: React.ReactNode;\n /**\n * The controlled form of the active tool: while set, the interaction hub's\n * active tool follows this value (re-applied when the document changes),\n * and `onToolChange` reports every change so the owner can update it. Omit\n * both for the uncontrolled default (`useInteraction().activateTool`).\n */\n tool?: string;\n /** Fires on every tool change of this stage's document (controlled or not). */\n onToolChange?: (toolId: string) => void;\n /** The stage lens to drive (default: the nearest `<StageScope>`, else the main StageToken). */\n token?: StageTokenProp;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport function Stage({\n children,\n pageChrome,\n overlay,\n tool,\n onToolChange,\n token: explicitToken,\n className,\n style,\n}: StageProps) {\n const token = useStageToken(explicitToken);\n // The surface is a host of the lens: it reports viewport size, drives gestures and\n // reads the lens id. The host contract is the same runtime token, typed wider.\n const stage = useCapability(token as unknown as CapabilityToken<StageHostCapability>);\n const ix = useOptionalCapability(InteractionToken);\n // Controlled tool: the prop is the source of truth whenever it is set.\n useEffect(() => {\n if (tool === undefined || !ix) return;\n if (ix.getActiveToolId() !== tool) ix.activateTool(tool);\n }, [tool, ix]);\n useCapabilityEvent(\n InteractionPublicToken,\n (interaction) => interaction.onToolChanged,\n (event) => onToolChange?.(event.toolId),\n );\n // How this view takes pointer input is its own settings (`interaction`,\n // `panFallback`, `zoomGestures`): a change rebinds the surface below.\n const interaction = useSelector(token, (stage) => stage.getSettings().interaction);\n const panFallback = useSelector(token, (stage) => stage.getSettings().panFallback);\n const zoomGestures = useSelector(token, (stage) => stage.getSettings().zoomGestures);\n // Routing pointer input to the interaction hub (page-resolved) also\n // registers this view's tool-gated pan-scroll handler with it, scoped to\n // the view, so two Stages on one document never pan each other. Without\n // the hub the Stage falls back to its own drag-to-pan.\n const useHub = interaction && !!ix;\n // The hub's resolved cursor (text/grab/…), applied to the viewport when driving.\n const hubCursor = useKernelValue(() => ix?.getCursor() ?? 'default');\n const ref = useRef<HTMLDivElement>(null);\n const docId = useDocumentId();\n // visiblePages already folds in the camera (each page carries its device-snapped\n // screenX/screenY + transform), so panning re-emits the list — no separate\n // camera subscription needed for positioning.\n const pages = useSelector(token, (stage) => stage.listVisiblePages()); // memoized -> stable ref\n // Reserved chrome bands (screen px), uniform across pages — the frame the\n // outer box reserves and `pageChrome` paints into.\n const frame = useSelector(\n token,\n (stage) => stage.getSettings().pageFrame,\n (left, right) =>\n left.top === right.top &&\n left.right === right.right &&\n left.bottom === right.bottom &&\n left.left === right.left,\n );\n\n // The Stage's ViewProjector: anchored UI (menus, popovers) positions through\n // the camera — pure state, no DOM reads, no portal, and no subscription:\n // `pages` (visiblePages) is the binding's revision, so a camera change\n // re-renders the pages and every anchored consumer in the same React\n // commit — surface and overlay can never paint a frame apart.\n const projector = useMemo<ViewProjector>(() => stageViewProjector(() => stage), [stage]);\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: pages }),\n [projector, pages],\n );\n // The pages on screen as one value that changes only when a page comes or\n // goes, so anchored UI on the other pages sits out every camera frame.\n const shownKey = pages.map((page) => page.ref.objectNumber).join(',');\n const shownPages = useMemo<ShownPages>(\n () => new Set(shownKey ? shownKey.split(',').map(Number) : []),\n [shownKey],\n );\n\n useLayoutEffect(() => {\n const element = ref.current!;\n // The whole browser binding — viewport/DPR reporting, sample normalization,\n // gesture controller — is the shared @embedpdf/web surface, so every\n // framework adapter has one feel. This component keeps only React glue.\n const detachSurface = createStageSurface(element, stage, {\n hub: useHub ? ix : null,\n source: stage.getLensId(),\n zoomGestures,\n });\n // Interaction opt-in lives with the sample source: the same knob that\n // forwards this lens's samples also registers its pan-scroll handler,\n // lens-scoped — two stages on one document can never pan each other.\n const offScroll =\n useHub && ix\n ? ix.registerHandler(createScrollHandler(stage, ix, { panFallback }), {\n source: stage.getLensId(),\n })\n : null;\n\n return () => {\n offScroll?.();\n detachSurface();\n };\n }, [stage, ix, useHub, zoomGestures, panFallback]);\n\n return (\n <div\n ref={ref}\n className={className}\n style={{\n position: 'relative',\n overflow: 'hidden',\n touchAction: 'none',\n ...(useHub ? { cursor: hubCursor } : null),\n ...style,\n }}\n >\n {/* Everything inside binds to this lens by default: a `useStageState()` in\n a page's chrome or a `<SelectionHandles>` in the overlay needs no token. */}\n <StageScope token={token}>\n {pages.map((visiblePage) => (\n <PageSurface\n key={visiblePage.ref.objectNumber} // durable page identity — survives move/delete (matches Angular's `track visiblePage.ref.objectNumber`)\n documentId={docId ?? ''}\n page={visiblePage}\n frame={frame}\n stage={stage}\n render={children}\n chrome={pageChrome}\n />\n ))}\n {/* Anchored UI mounts in the overlay: absolute coords here are the\n projector's overlay space (the stage container). */}\n <ShownPagesProvider value={shownPages}>\n <ProjectorProvider value={projectorBinding}>{overlay}</ProjectorProvider>\n </ShownPagesProvider>\n </StageScope>\n </div>\n );\n}\n\n// ── The hooks ────────────────────────────────────────────────────────────────\n// Every hook takes an optional token; without one it binds to the nearest\n// `<StageScope>` / `<Stage>`, else the main view.\n\n/** The Stage's API (zoom, navigation, reveal, settings), for app chrome. */\nexport function useStage(token?: StageTokenProp): StageCapability {\n return useCapability(useStageToken(token));\n}\n\n/** Subscribe to one stage event for the mounted lifetime: `useStageEvent((stage) => stage.onZoomChanged, handler)`. */\nexport function useStageEvent<T>(\n select: (stage: StageCapability) => EventHook<T>,\n handler: (event: T) => void,\n token?: StageTokenProp,\n): void {\n useCapabilityEvent(useStageToken(token), select, handler);\n}\n\n/** What `useStageState()` returns: the Stage page's State table. */\ntype StageStateValue = (typeof stageState)['empty'];\n\n/**\n * The view's state: the zoom, the current page, the page count, the view\n * rotation and the responsive rules that apply (the page's State table,\n * declared once in `stageState`). Takes a selector, and re-renders only when\n * what it returns changes. Without a document it is `stageState.empty`.\n */\nexport function useStageState<Selected = StageStateValue>(\n select?: (state: StageStateValue) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n // Resolved on every read: a document that closes reads as `empty`, never\n // through its closed capability.\n return useKernelValue((kernel) => {\n const stage = kernel.tryCapability(lens, scoped ?? undefined);\n const state = stage ? stageState.read(stage) : stageState.empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n}\n\n/**\n * The view's settings, or the value `select` picks from them. They belong to\n * the view, so without a document there is none yet and this reads the\n * defaults. Change them with `useStage().updateSettings()`.\n */\nexport function useStageSettings<Selected = StageSettings>(\n select?: (settings: StageSettings) => Selected,\n token?: StageTokenProp,\n): Selected {\n const lens = useStageToken(token);\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => {\n const settings = kernel.tryCapability(lens, scoped ?? undefined)?.getSettings();\n const value = settings ?? DEFAULT_SETTINGS;\n return select ? select(value) : (value as unknown as Selected);\n }, shallowEqual);\n}\n"],"mappings":"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"}
|
package/dist/stage.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_stage_scope = require("./stage-scope-CQgCuf1w.cjs");
|
|
3
|
-
const require_stage = require("./stage-
|
|
3
|
+
const require_stage = require("./stage-B7BG2e8Q.cjs");
|
|
4
4
|
exports.Scrollbar = require_stage.Scrollbar;
|
|
5
5
|
exports.Stage = require_stage.Stage;
|
|
6
6
|
exports.StageScope = require_stage_scope.StageScope;
|
package/dist/stage.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { n as useStageToken, t as StageScope } from "./stage-scope-D7ZeNduT.js";
|
|
2
|
-
import { a as useStageState, i as useStageSettings, n as useStage, o as Scrollbar, r as useStageEvent, s as useScrollMetrics, t as Stage } from "./stage-
|
|
2
|
+
import { a as useStageState, i as useStageSettings, n as useStage, o as Scrollbar, r as useStageEvent, s as useScrollMetrics, t as Stage } from "./stage-BqfBjDu6.js";
|
|
3
3
|
export * from "@embedpdf/plugin-stage";
|
|
4
4
|
export { Scrollbar, Stage, StageScope, useScrollMetrics, useStage, useStageEvent, useStageSettings, useStageState, useStageToken };
|
package/dist/stamp.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./rolldown-runtime-DDXJien1.cjs");
|
|
3
|
-
const require_runtime = require("./runtime-
|
|
4
|
-
const require_state = require("./state-
|
|
3
|
+
const require_runtime = require("./runtime-BfnpyfY0.cjs");
|
|
4
|
+
const require_state = require("./state-Dtf8HAue.cjs");
|
|
5
5
|
let _embedpdf_web = require("@embedpdf/web");
|
|
6
6
|
let react = require("react");
|
|
7
7
|
let _embedpdf_plugin_stamp = require("@embedpdf/plugin-stamp");
|
package/dist/stamp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as useSelector, f as useCapability, p as useCapabilityEvent, u as shallowArray } from "./runtime-
|
|
2
|
-
import { n as stateHook, t as settingsHook } from "./state-
|
|
1
|
+
import { E as useSelector, f as useCapability, p as useCapabilityEvent, u as shallowArray } from "./runtime-tD5iJU83.js";
|
|
2
|
+
import { n as stateHook, t as settingsHook } from "./state-CzI2KlZb.js";
|
|
3
3
|
import { indexedDbByteStore, objectUrlOf } from "@embedpdf/web";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
5
|
import { StampToken, stampState } from "@embedpdf/plugin-stamp";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as useDocumentScope, x as useKernelValue } from "./runtime-
|
|
1
|
+
import { g as useDocumentScope, x as useKernelValue } from "./runtime-tD5iJU83.js";
|
|
2
2
|
import { shallowEqual } from "@embedpdf/core";
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
//#region src/state.ts
|
|
@@ -59,4 +59,4 @@ function settingsHook(token) {
|
|
|
59
59
|
//#endregion
|
|
60
60
|
export { stateHook as n, settingsHook as t };
|
|
61
61
|
|
|
62
|
-
//# sourceMappingURL=state-
|
|
62
|
+
//# sourceMappingURL=state-CzI2KlZb.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-
|
|
1
|
+
{"version":3,"file":"state-CzI2KlZb.js","names":[],"sources":["../src/state.ts"],"sourcesContent":["/**\n * Declared state and settings as React hooks. A plugin declares its state once\n * with `defineState` and its settings once in its definition (both in\n * `@embedpdf/core`), and its React bindings are one line each:\n *\n * export const useSearchState = stateHook(searchState);\n * export const useSearchSettings = settingsHook(SearchToken);\n */\nimport { useEffect } from 'react';\nimport { shallowEqual } from '@embedpdf/core';\nimport type { CapabilityToken, SettingsOf, StateDeclaration } from '@embedpdf/core';\nimport { useDocumentScope, useKernelValue } from './runtime';\n\n/** A declared state's hook: the whole state, or the value `select` picks from it. */\nexport type StateHook<State> = <Selected = State>(select?: (state: State) => Selected) => Selected;\n\n/** A plugin's settings hook: the settings, or the value `select` picks from them. */\nexport type SettingsHook<Settings> = <Selected = Settings>(\n select?: (settings: Settings) => Selected,\n) => Selected;\n\n/**\n * Turn a state declaration into its hook. The hook reads this subtree's\n * document (the nearest <DocumentScope>, else the active one) and returns the\n * declaration's `empty` while that document is missing or not ready, so it\n * renders anywhere. It re-renders only when a field changes: the state, or the\n * value `select` picks from it, is compared with {@link shallowEqual}. State\n * the plugin loads on demand (the declaration's `ensure`) loads once a ready\n * document's capability is in scope, for every document the hook reads.\n */\nexport function stateHook<Capability, State extends object>(\n declaration: StateDeclaration<Capability, State>,\n): StateHook<State> {\n const { ensure } = declaration;\n const useDeclaredState = function useDeclaredState<Selected = State>(\n select?: (state: State) => Selected,\n ): Selected {\n const scoped = useDocumentScope();\n // Resolve on every read, not once per render: a document that closes\n // between a store change and the next render then reads as `empty`,\n // never through its closed capability.\n return useKernelValue((kernel) => {\n const capability = kernel.tryCapability(declaration.token, scoped ?? undefined);\n const state = capability ? declaration.read(capability) : declaration.empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n };\n if (!ensure) return useDeclaredState;\n return function useLoadedState<Selected = State>(select?: (state: State) => Selected): Selected {\n const scoped = useDocumentScope();\n const capability = useKernelValue(\n (kernel) => kernel.tryCapability(declaration.token, scoped ?? undefined),\n Object.is,\n );\n // The state says when the load fails (its status), so the rejection has nothing to add.\n useEffect(() => {\n if (capability) ensure(capability).catch(() => {});\n }, [capability]);\n return useDeclaredState(select);\n };\n}\n\n/**\n * Turn a plugin's token into its settings hook. A plugin's settings belong to\n * the plugin as the app registered it, not to a document, so the hook reads\n * them through the kernel (`kernel.settingsOf`): it works before the first\n * document opens, and every document shows the same settings. It re-renders\n * only when a setting changes: `getSettings()` keeps its object until then,\n * and the value `select` picks is compared with {@link shallowEqual}. A token\n * whose plugin isn't installed, or has no settings, throws: a setup mistake.\n */\nexport function settingsHook<Capability>(\n token: CapabilityToken<Capability>,\n): SettingsHook<SettingsOf<Capability>> {\n return function useSettings<Selected = SettingsOf<Capability>>(\n select?: (settings: SettingsOf<Capability>) => Selected,\n ): Selected {\n return useKernelValue((kernel) => {\n const settings = kernel.settingsOf(token).getSettings();\n return select ? select(settings) : (settings as unknown as Selected);\n }, shallowEqual);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,UACd,aACkB;CAClB,MAAM,EAAE,WAAW;CACnB,MAAM,mBAAmB,SAAS,iBAChC,QACU;EACV,MAAM,SAAS,iBAAiB;EAIhC,OAAO,gBAAgB,WAAW;GAChC,MAAM,aAAa,OAAO,cAAc,YAAY,OAAO,UAAU,KAAA,CAAS;GAC9E,MAAM,QAAQ,aAAa,YAAY,KAAK,UAAU,IAAI,YAAY;GACtE,OAAO,SAAS,OAAO,KAAK,IAAK;EACnC,GAAG,YAAY;CACjB;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,SAAS,eAAiC,QAA+C;EAC9F,MAAM,SAAS,iBAAiB;EAChC,MAAM,aAAa,gBAChB,WAAW,OAAO,cAAc,YAAY,OAAO,UAAU,KAAA,CAAS,GACvE,OAAO,EACT;EAEA,gBAAgB;GACd,IAAI,YAAY,OAAO,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC;EACnD,GAAG,CAAC,UAAU,CAAC;EACf,OAAO,iBAAiB,MAAM;CAChC;AACF;;;;;;;;;;AAWA,SAAgB,aACd,OACsC;CACtC,OAAO,SAAS,YACd,QACU;EACV,OAAO,gBAAgB,WAAW;GAChC,MAAM,WAAW,OAAO,WAAW,KAAK,CAAC,CAAC,YAAY;GACtD,OAAO,SAAS,OAAO,QAAQ,IAAK;EACtC,GAAG,YAAY;CACjB;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_runtime = require("./runtime-
|
|
1
|
+
const require_runtime = require("./runtime-BfnpyfY0.cjs");
|
|
2
2
|
let _embedpdf_core = require("@embedpdf/core");
|
|
3
3
|
let react = require("react");
|
|
4
4
|
//#region src/state.ts
|
|
@@ -70,4 +70,4 @@ Object.defineProperty(exports, "stateHook", {
|
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
//# sourceMappingURL=state-
|
|
73
|
+
//# sourceMappingURL=state-Dtf8HAue.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-
|
|
1
|
+
{"version":3,"file":"state-Dtf8HAue.cjs","names":["useDocumentScope","useKernelValue","shallowEqual"],"sources":["../src/state.ts"],"sourcesContent":["/**\n * Declared state and settings as React hooks. A plugin declares its state once\n * with `defineState` and its settings once in its definition (both in\n * `@embedpdf/core`), and its React bindings are one line each:\n *\n * export const useSearchState = stateHook(searchState);\n * export const useSearchSettings = settingsHook(SearchToken);\n */\nimport { useEffect } from 'react';\nimport { shallowEqual } from '@embedpdf/core';\nimport type { CapabilityToken, SettingsOf, StateDeclaration } from '@embedpdf/core';\nimport { useDocumentScope, useKernelValue } from './runtime';\n\n/** A declared state's hook: the whole state, or the value `select` picks from it. */\nexport type StateHook<State> = <Selected = State>(select?: (state: State) => Selected) => Selected;\n\n/** A plugin's settings hook: the settings, or the value `select` picks from them. */\nexport type SettingsHook<Settings> = <Selected = Settings>(\n select?: (settings: Settings) => Selected,\n) => Selected;\n\n/**\n * Turn a state declaration into its hook. The hook reads this subtree's\n * document (the nearest <DocumentScope>, else the active one) and returns the\n * declaration's `empty` while that document is missing or not ready, so it\n * renders anywhere. It re-renders only when a field changes: the state, or the\n * value `select` picks from it, is compared with {@link shallowEqual}. State\n * the plugin loads on demand (the declaration's `ensure`) loads once a ready\n * document's capability is in scope, for every document the hook reads.\n */\nexport function stateHook<Capability, State extends object>(\n declaration: StateDeclaration<Capability, State>,\n): StateHook<State> {\n const { ensure } = declaration;\n const useDeclaredState = function useDeclaredState<Selected = State>(\n select?: (state: State) => Selected,\n ): Selected {\n const scoped = useDocumentScope();\n // Resolve on every read, not once per render: a document that closes\n // between a store change and the next render then reads as `empty`,\n // never through its closed capability.\n return useKernelValue((kernel) => {\n const capability = kernel.tryCapability(declaration.token, scoped ?? undefined);\n const state = capability ? declaration.read(capability) : declaration.empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n };\n if (!ensure) return useDeclaredState;\n return function useLoadedState<Selected = State>(select?: (state: State) => Selected): Selected {\n const scoped = useDocumentScope();\n const capability = useKernelValue(\n (kernel) => kernel.tryCapability(declaration.token, scoped ?? undefined),\n Object.is,\n );\n // The state says when the load fails (its status), so the rejection has nothing to add.\n useEffect(() => {\n if (capability) ensure(capability).catch(() => {});\n }, [capability]);\n return useDeclaredState(select);\n };\n}\n\n/**\n * Turn a plugin's token into its settings hook. A plugin's settings belong to\n * the plugin as the app registered it, not to a document, so the hook reads\n * them through the kernel (`kernel.settingsOf`): it works before the first\n * document opens, and every document shows the same settings. It re-renders\n * only when a setting changes: `getSettings()` keeps its object until then,\n * and the value `select` picks is compared with {@link shallowEqual}. A token\n * whose plugin isn't installed, or has no settings, throws: a setup mistake.\n */\nexport function settingsHook<Capability>(\n token: CapabilityToken<Capability>,\n): SettingsHook<SettingsOf<Capability>> {\n return function useSettings<Selected = SettingsOf<Capability>>(\n select?: (settings: SettingsOf<Capability>) => Selected,\n ): Selected {\n return useKernelValue((kernel) => {\n const settings = kernel.settingsOf(token).getSettings();\n return select ? select(settings) : (settings as unknown as Selected);\n }, shallowEqual);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,UACd,aACkB;CAClB,MAAM,EAAE,WAAW;CACnB,MAAM,mBAAmB,SAAS,iBAChC,QACU;EACV,MAAM,SAASA,gBAAAA,iBAAiB;EAIhC,OAAOC,gBAAAA,gBAAgB,WAAW;GAChC,MAAM,aAAa,OAAO,cAAc,YAAY,OAAO,UAAU,KAAA,CAAS;GAC9E,MAAM,QAAQ,aAAa,YAAY,KAAK,UAAU,IAAI,YAAY;GACtE,OAAO,SAAS,OAAO,KAAK,IAAK;EACnC,GAAGC,eAAAA,YAAY;CACjB;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,SAAS,eAAiC,QAA+C;EAC9F,MAAM,SAASF,gBAAAA,iBAAiB;EAChC,MAAM,aAAaC,gBAAAA,gBAChB,WAAW,OAAO,cAAc,YAAY,OAAO,UAAU,KAAA,CAAS,GACvE,OAAO,EACT;EAEA,CAAA,GAAA,MAAA,UAAA,OAAgB;GACd,IAAI,YAAY,OAAO,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC;EACnD,GAAG,CAAC,UAAU,CAAC;EACf,OAAO,iBAAiB,MAAM;CAChC;AACF;;;;;;;;;;AAWA,SAAgB,aACd,OACsC;CACtC,OAAO,SAAS,YACd,QACU;EACV,OAAOA,gBAAAA,gBAAgB,WAAW;GAChC,MAAM,WAAW,OAAO,WAAW,KAAK,CAAC,CAAC,YAAY;GACtD,OAAO,SAAS,OAAO,QAAQ,IAAK;EACtC,GAAGC,eAAAA,YAAY;CACjB;AACF"}
|
package/dist/toolbar.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-DDXJien1.cjs");
|
|
3
|
-
const require_runtime = require("./runtime-
|
|
3
|
+
const require_runtime = require("./runtime-BfnpyfY0.cjs");
|
|
4
4
|
const require_inert = require("./inert-CmBEbKCn.cjs");
|
|
5
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
6
|
let _embedpdf_web = require("@embedpdf/web");
|
package/dist/toolbar.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as useKernel, f as useCapability, h as useDocumentId, x as useKernelValue } from "./runtime-
|
|
1
|
+
import { b as useKernel, f as useCapability, h as useDocumentId, x as useKernelValue } from "./runtime-tD5iJU83.js";
|
|
2
2
|
import { t as INERT } from "./inert-D8fkennb.js";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { TOOLBAR_ITEM_ATTRIBUTE, attachMenuKeys, attachToolbarFocus, createToolbarLengths, focusablesIn, observeContentLength, observeLength, observeOutsidePress, paintDefault, toolbarMeasureKey } from "@embedpdf/web";
|
package/dist/view-manager.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./rolldown-runtime-DDXJien1.cjs");
|
|
3
|
-
const require_runtime = require("./runtime-
|
|
4
|
-
const require_state = require("./state-
|
|
3
|
+
const require_runtime = require("./runtime-BfnpyfY0.cjs");
|
|
4
|
+
const require_state = require("./state-Dtf8HAue.cjs");
|
|
5
5
|
let _embedpdf_plugin_view_manager = require("@embedpdf/plugin-view-manager");
|
|
6
6
|
//#region src/view-manager.ts
|
|
7
7
|
/** The view-manager API (panes and tabs) for app code. Its object never changes. */
|
package/dist/view-manager.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { f as useCapability, p as useCapabilityEvent } from "./runtime-
|
|
2
|
-
import { n as stateHook } from "./state-
|
|
1
|
+
import { f as useCapability, p as useCapabilityEvent } from "./runtime-tD5iJU83.js";
|
|
2
|
+
import { n as stateHook } from "./state-CzI2KlZb.js";
|
|
3
3
|
import { ViewManagerToken, viewManagerState } from "@embedpdf/plugin-view-manager";
|
|
4
4
|
export * from "@embedpdf/plugin-view-manager";
|
|
5
5
|
//#region src/view-manager.ts
|