@embedpdf/plugin-ui 2.1.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +85 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +11 -1
- package/dist/lib/types.d.ts +25 -0
- package/dist/lib/ui-plugin.d.ts +6 -0
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +8 -2
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +8 -2
- package/dist/react/index.js.map +1 -1
- package/dist/shared/hooks/use-schema-renderer.d.ts +1 -0
- package/dist/shared-preact/hooks/use-schema-renderer.d.ts +1 -0
- package/dist/shared-react/hooks/use-schema-renderer.d.ts +1 -0
- package/dist/svelte/hooks/use-schema-renderer.svelte.d.ts +1 -0
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +9 -2
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/hooks/use-schema-renderer.d.ts +1 -0
- package/dist/vue/hooks/use-ui.d.ts +6 -0
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +10 -2
- package/dist/vue/index.js.map +1 -1
- package/package.json +12 -12
package/dist/svelte/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/svelte/hooks/use-ui.svelte.ts","../../src/svelte/hooks/use-ui-container.svelte.ts","../../src/svelte/registries/anchor-registry.svelte.ts","../../src/svelte/hooks/use-register-anchor.svelte.ts","../../src/svelte/registries/component-registry.svelte.ts","../../src/svelte/hooks/use-item-renderer.svelte.ts","../../src/svelte/registries/renderers-registry.svelte.ts","../../src/svelte/hooks/use-schema-renderer.svelte.ts","../../src/svelte/hooks/use-selection-menu.svelte.ts","../../src/svelte/auto-menu-renderer.svelte","../../src/svelte/root.svelte","../../src/svelte/provider.svelte"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\nimport { UIPlugin, UIDocumentState, UIScope } from '@embedpdf/plugin-ui';\n\n/**\n * Hook to get the raw UI plugin instance.\n */\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\n\n/**\n * Hook to get the UI plugin's capability API.\n */\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\n\n// Define the return type explicitly to maintain type safety\ninterface UseUIStateReturn {\n provides: UIScope | null;\n state: UIDocumentState | null;\n}\n\n/**\n * Hook for UI state for a specific document\n * @param getDocumentId Function that returns the document ID\n */\nexport const useUIState = (getDocumentId: () => string | null): UseUIStateReturn => {\n const capability = useUICapability();\n\n let state = $state<UIDocumentState | null>(null);\n\n // Reactive documentId\n const documentId = $derived(getDocumentId());\n\n // Scoped capability for current docId\n const scopedProvides = $derived(\n capability.provides && documentId ? capability.provides.forDocument(documentId) : null,\n );\n\n $effect(() => {\n const provides = capability.provides;\n const docId = documentId;\n\n if (!provides || !docId) {\n state = null;\n return;\n }\n\n const scope = provides.forDocument(docId);\n\n // Set initial state\n state = scope.getState();\n\n // Subscribe to all changes and update state\n const unsubToolbar = scope.onToolbarChanged(() => {\n state = scope.getState();\n });\n const unsubSidebar = scope.onSidebarChanged(() => {\n state = scope.getState();\n });\n const unsubModal = scope.onModalChanged(() => {\n state = scope.getState();\n });\n const unsubMenu = scope.onMenuChanged(() => {\n state = scope.getState();\n });\n\n return () => {\n unsubToolbar();\n unsubSidebar();\n unsubModal();\n unsubMenu();\n };\n });\n\n return {\n get provides() {\n return scopedProvides;\n },\n get state() {\n return state;\n },\n };\n};\n\n/**\n * Hook to get UI schema\n * Returns an object with a reactive getter for the schema\n */\nexport const useUISchema = () => {\n const capability = useUICapability();\n\n return {\n get schema() {\n return capability.provides?.getSchema() ?? null;\n },\n };\n};\n","import { getContext, setContext } from 'svelte';\n\nexport interface UIContainerContextValue {\n /** Get the container element (may be null if not mounted) */\n getContainer: () => HTMLDivElement | null;\n}\n\nconst UI_CONTAINER_KEY = Symbol('ui-container');\n\n/**\n * Set up the container context (called by UIRoot)\n */\nexport function setUIContainerContext(value: UIContainerContextValue): void {\n setContext(UI_CONTAINER_KEY, value);\n}\n\n/**\n * Hook to access the UI container element.\n *\n * This provides access to the UIRoot container for:\n * - Container query based responsiveness\n * - Portaling elements to the root\n * - Measuring container dimensions\n *\n * @example\n * ```svelte\n * <script>\n * import { useUIContainer } from '@embedpdf/plugin-ui/svelte';\n * import { onMount, onDestroy } from 'svelte';\n *\n * const { getContainer } = useUIContainer();\n *\n * let observer;\n *\n * onMount(() => {\n * const container = getContainer();\n * if (!container) return;\n *\n * observer = new ResizeObserver(() => {\n * console.log('Container width:', container.clientWidth);\n * });\n * observer.observe(container);\n * });\n *\n * onDestroy(() => observer?.disconnect());\n * </script>\n * ```\n */\nexport function useUIContainer(): UIContainerContextValue {\n const context = getContext<UIContainerContextValue>(UI_CONTAINER_KEY);\n if (!context) {\n throw new Error('useUIContainer must be used within a UIProvider');\n }\n return context;\n}\n","import { getContext, setContext } from 'svelte';\n\n/**\n * Anchor Registry\n *\n * Tracks DOM elements for menu positioning.\n * Each anchor is scoped by documentId and itemId.\n */\nexport interface AnchorRegistry {\n register(documentId: string, itemId: string, element: HTMLElement): void;\n unregister(documentId: string, itemId: string): void;\n getAnchor(documentId: string, itemId: string): HTMLElement | null;\n}\n\nconst ANCHOR_REGISTRY_KEY = Symbol('AnchorRegistry');\n\nexport function createAnchorRegistry(): AnchorRegistry {\n const anchors = new Map<string, HTMLElement>();\n\n return {\n register(documentId: string, itemId: string, element: HTMLElement) {\n const key = `${documentId}:${itemId}`;\n anchors.set(key, element);\n },\n\n unregister(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n anchors.delete(key);\n },\n\n getAnchor(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n return anchors.get(key) || null;\n },\n };\n}\n\nexport function provideAnchorRegistry() {\n const registry = createAnchorRegistry();\n setContext(ANCHOR_REGISTRY_KEY, registry);\n return registry;\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const registry = getContext<AnchorRegistry>(ANCHOR_REGISTRY_KEY);\n if (!registry) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { onDestroy } from 'svelte';\nimport { useAnchorRegistry } from '../registries/anchor-registry.svelte';\n\n/**\n * Register a DOM element as an anchor for menus\n *\n * @param getDocumentId - Function returning document ID\n * @param getItemId - Function returning item ID (typically matches the toolbar/menu item ID)\n * @returns Function to attach to the element via use:action\n *\n * @example\n *\n * <script lang=\"ts\">\n * const registerAnchor = useRegisterAnchor(() => documentId, () => 'zoom-button');\n * </script>\n *\n * <button use:registerAnchor>Zoom</button>\n * */\nexport function useRegisterAnchor(getDocumentId: () => string | null, getItemId: () => string) {\n const registry = useAnchorRegistry();\n let currentElement = $state<HTMLElement | null>(null);\n\n // Reactive values - these update when the functions return different values\n const documentId = $derived(getDocumentId());\n const itemId = $derived(getItemId());\n\n // Re-register anchor when documentId, itemId, or element changes\n $effect(() => {\n const docId = documentId;\n const item = itemId;\n const element = currentElement;\n\n // Only register if we have all required values\n if (element && docId && item) {\n registry.register(docId, item, element);\n\n // Cleanup: unregister when effect re-runs or component unmounts\n return () => {\n registry.unregister(docId, item);\n };\n }\n });\n\n // Svelte action function\n const action = (element: HTMLElement) => {\n currentElement = element;\n\n return {\n destroy() {\n // Clear the element reference when the action is destroyed\n currentElement = null;\n },\n };\n };\n\n return action;\n}\n","import { getContext, setContext, type Component } from 'svelte';\nimport type { BaseComponentProps } from '../types';\n\n/**\n * Component Registry\n *\n * Stores custom components that can be referenced in the UI schema.\n */\nexport interface ComponentRegistry {\n register(id: string, component: Component<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): Component<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst COMPONENT_REGISTRY_KEY = Symbol('ComponentRegistry');\n\nexport function createComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n): ComponentRegistry {\n const components = new Map<string, Component<BaseComponentProps>>(\n Object.entries(initialComponents),\n );\n\n return {\n register(id: string, component: Component<BaseComponentProps>) {\n components.set(id, component);\n },\n\n unregister(id: string) {\n components.delete(id);\n },\n\n get(id: string) {\n return components.get(id);\n },\n\n has(id: string) {\n return components.has(id);\n },\n\n getRegisteredIds() {\n return Array.from(components.keys());\n },\n };\n}\n\nexport function provideComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n) {\n const registry = createComponentRegistry(initialComponents);\n setContext(COMPONENT_REGISTRY_KEY, registry);\n return registry;\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const registry = getContext<ComponentRegistry>(COMPONENT_REGISTRY_KEY);\n if (!registry) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { useComponentRegistry } from '../registries/component-registry.svelte';\n\n/**\n * Helper utilities for building renderers\n */\nexport function useItemRenderer() {\n const componentRegistry = useComponentRegistry();\n\n return {\n /**\n * Get a custom component by ID\n *\n * @param componentId - Component ID from schema\n * @returns Component constructor or undefined if not found\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const { getCustomComponent } = useItemRenderer();\n * const MyComponent = getCustomComponent('my-component-id');\n * </script>\n *\n * {#if MyComponent}\n * <MyComponent {documentId} {...props} />\n * {/if}\n * ```\n */\n getCustomComponent: (componentId: string) => {\n const Component = componentRegistry.get(componentId);\n\n if (!Component) {\n console.error(`Component \"${componentId}\" not found in registry`);\n return undefined;\n }\n\n return Component;\n },\n };\n}\n","import { getContext, setContext } from 'svelte';\nimport type { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RENDERERS_KEY = Symbol('Renderers');\n\nexport function provideRenderers(renderers: UIRenderers) {\n setContext(RENDERERS_KEY, renderers);\n}\n\nexport function useRenderers(): UIRenderers {\n const renderers = getContext<UIRenderers>(RENDERERS_KEY);\n if (!renderers) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return renderers;\n}\n","import { useUICapability } from './use-ui.svelte';\nimport { useRenderers } from '../registries/renderers-registry.svelte';\n\n/**\n * High-level hook for rendering UI from schema\n *\n * Provides information about active toolbars, sidebars, and modals.\n * Always includes isOpen state so renderers can control animations.\n *\n * Use with Svelte's component binding to render toolbars and sidebars.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const { getToolbarInfo, getSidebarInfo, getModalInfo } = useSchemaRenderer(() => documentId);\n *\n * const topMainToolbar = $derived(getToolbarInfo('top', 'main'));\n * const leftMainSidebar = $derived(getSidebarInfo('left', 'main'));\n * const modal = $derived(getModalInfo());\n * </script>\n *\n * {#if topMainToolbar}\n * {@const ToolbarRenderer = topMainToolbar.renderer}\n * <ToolbarRenderer\n * schema={topMainToolbar.schema}\n * documentId={topMainToolbar.documentId}\n * isOpen={topMainToolbar.isOpen}\n * onClose={topMainToolbar.onClose}\n * />\n * {/if}\n * ```\n */\nexport function useSchemaRenderer(getDocumentId: () => string | null) {\n const renderers = useRenderers();\n const capability = useUICapability();\n const uiState = useUIState(getDocumentId);\n\n return {\n /**\n * Get toolbar information by placement and slot\n *\n * @param placement - 'top' | 'bottom' | 'left' | 'right'\n * @param slot - Slot name (e.g. 'main', 'secondary')\n * @returns Toolbar info or null if no toolbar in slot\n */\n getToolbarInfo: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string) => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema || !uiState.provides || !uiState.state || !documentId) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.state.activeToolbars[slotKey];\n\n // If no toolbar in this slot, nothing to render\n if (!toolbarSlot) return null;\n\n const toolbarSchema = schema.toolbars[toolbarSlot.toolbarId];\n if (!toolbarSchema) {\n console.warn(`Toolbar \"${toolbarSlot.toolbarId}\" not found in schema`);\n return null;\n }\n\n // Check if toolbar is closable\n const isClosable = !toolbarSchema.permanent;\n\n const handleClose = isClosable\n ? () => {\n uiState.provides?.closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n return {\n renderer: renderers.toolbar,\n schema: toolbarSchema,\n documentId,\n isOpen: toolbarSlot.isOpen,\n onClose: handleClose,\n };\n },\n\n /**\n * Get sidebar information by placement and slot\n *\n * @param placement - 'left' | 'right' | 'top' | 'bottom'\n * @param slot - Slot name (e.g. 'main', 'secondary', 'inspector')\n * @returns Sidebar info or null if no sidebar in slot\n */\n getSidebarInfo: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string) => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema || !uiState.provides || !uiState.state || !documentId) return null;\n\n const slotKey = `${placement}-${slot}`;\n const sidebarSlot = uiState.state.activeSidebars[slotKey];\n\n // If no sidebar in this slot, nothing to render\n if (!sidebarSlot) return null;\n\n const sidebarSchema = schema.sidebars?.[sidebarSlot.sidebarId];\n if (!sidebarSchema) {\n console.warn(`Sidebar \"${sidebarSlot.sidebarId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n uiState.provides?.closeSidebarSlot(placement, slot);\n };\n\n return {\n renderer: renderers.sidebar,\n schema: sidebarSchema,\n documentId,\n isOpen: sidebarSlot.isOpen,\n onClose: handleClose,\n };\n },\n\n /**\n * Get modal information (if active)\n *\n * Supports animation lifecycle:\n * - isOpen: true = visible\n * - isOpen: false = animate out (modal still rendered)\n * - onExited called after animation → modal removed\n *\n * @returns Modal info or null if no modal active\n */\n getModalInfo: () => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema || !uiState.provides || !uiState.state?.activeModal || !documentId) return null;\n\n const { modalId, isOpen } = uiState.state.activeModal;\n\n const modalSchema = schema.modals?.[modalId];\n if (!modalSchema) {\n console.warn(`Modal \"${modalId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n uiState.provides?.closeModal();\n };\n\n const handleExited = () => {\n uiState.provides?.clearModal();\n };\n\n const ModalRenderer = renderers.modal;\n if (!ModalRenderer) {\n console.warn('No modal renderer registered');\n return null;\n }\n\n return {\n renderer: ModalRenderer,\n schema: modalSchema,\n documentId,\n isOpen,\n onClose: handleClose,\n onExited: handleExited,\n };\n },\n\n /**\n * Helper: Get all active toolbars for this document\n * Useful for batch rendering or debugging\n */\n getActiveToolbars: () => {\n if (!uiState.state) return [];\n return Object.entries(uiState.state.activeToolbars).map(([slotKey, toolbarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n toolbarId: toolbarSlot.toolbarId,\n isOpen: toolbarSlot.isOpen,\n };\n });\n },\n\n /**\n * Helper: Get all active sidebars for this document\n * Useful for batch rendering or debugging\n */\n getActiveSidebars: () => {\n if (!uiState.state) return [];\n return Object.entries(uiState.state.activeSidebars).map(([slotKey, sidebarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n sidebarId: sidebarSlot.sidebarId,\n isOpen: sidebarSlot.isOpen,\n };\n });\n },\n\n /**\n * Get overlay information for all enabled overlays\n *\n * Overlays are floating components positioned over the document content.\n * Unlike modals, multiple overlays can be visible and they don't block interaction.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const { getOverlaysInfo } = useSchemaRenderer(() => documentId);\n * const overlays = $derived(getOverlaysInfo());\n * </script>\n *\n * {#each overlays as overlay (overlay.schema.id)}\n * {@const OverlayRenderer = overlay.renderer}\n * <OverlayRenderer schema={overlay.schema} documentId={overlay.documentId} />\n * {/each}\n * ```\n */\n getOverlaysInfo: () => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema?.overlays || !documentId) return [];\n\n const OverlayRenderer = renderers.overlay;\n if (!OverlayRenderer) {\n return [];\n }\n\n return Object.values(schema.overlays).map((overlaySchema) => ({\n renderer: OverlayRenderer,\n schema: overlaySchema,\n documentId,\n }));\n },\n };\n}\n\n// Import after definition to avoid circular dependency\nimport { useUIState } from './use-ui.svelte';\n","import type {\n SelectionMenuPropsBase,\n SelectionMenuRenderFn,\n SelectionMenuRenderResult,\n} from '@embedpdf/utils/svelte';\nimport { useUICapability } from './use-ui.svelte';\nimport { useRenderers } from '../registries/renderers-registry.svelte';\n\n/**\n * Hook for schema-driven selection menus\n */\nexport function useSelectionMenu<TContext extends { type: string }>(\n menuId: string | (() => string),\n getDocumentId: () => string,\n) {\n const uiCapability = useUICapability();\n const renderers = useRenderers();\n\n // Normalize menuId to always be a function, then make it reactive\n const getMenuIdFn = typeof menuId === 'function' ? menuId : () => menuId;\n const menuIdValue = $derived(getMenuIdFn());\n const documentId = $derived(getDocumentId());\n const schema = $derived(uiCapability.provides?.getSchema());\n const menuSchema = $derived(schema?.selectionMenus?.[menuIdValue]);\n\n const renderFn = $derived.by<SelectionMenuRenderFn<TContext> | undefined>(() => {\n if (!menuSchema) return undefined;\n\n const currentMenuSchema = menuSchema;\n const currentDocumentId = documentId;\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n return (props: SelectionMenuPropsBase<TContext>): SelectionMenuRenderResult | null => {\n if (!props.selected) return null;\n\n return {\n component: SelectionMenuRenderer,\n props: {\n schema: currentMenuSchema,\n documentId: currentDocumentId,\n props,\n },\n };\n };\n });\n\n return {\n get renderFn() {\n return renderFn;\n },\n };\n}\n","<script lang=\"ts\">\n import { useUIState, useUICapability } from './hooks/use-ui.svelte';\n import { useAnchorRegistry } from './registries/anchor-registry.svelte';\n import { useRenderers } from './registries/renderers-registry.svelte';\n\n /**\n * Automatically renders menus when opened\n *\n * This component:\n * 1. Listens to UI plugin state for open menus\n * 2. Looks up anchor elements from the anchor registry\n * 3. Renders menus using the user-provided menu renderer\n */\n\n interface Props {\n documentId: string; // Which document's menus to render\n container?: HTMLElement | null;\n }\n\n let { documentId, container = null }: Props = $props();\n\n const uiState = useUIState(() => documentId);\n const capability = useUICapability();\n const anchorRegistry = useAnchorRegistry();\n const renderers = useRenderers();\n\n // Derived state for active menu\n const activeMenu = $derived.by(() => {\n const openMenus = uiState.state?.openMenus || {};\n const openMenuIds = Object.keys(openMenus);\n\n if (openMenuIds.length === 0) return null;\n\n // Show the first open menu (in practice, should only be one)\n const menuId = openMenuIds[0];\n if (!menuId) return null;\n\n const menuState = openMenus[menuId];\n if (!menuState || !menuState.triggeredByItemId) return null;\n\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(documentId, menuState.triggeredByItemId);\n return { menuId, anchorEl: anchor };\n });\n\n const schema = $derived(capability.provides?.getSchema());\n\n const menuSchema = $derived.by(() => {\n if (!activeMenu || !schema) return null;\n\n const menuSchemaValue = schema.menus[activeMenu.menuId];\n if (!menuSchemaValue) {\n console.warn(`Menu \"${activeMenu.menuId}\" not found in schema`);\n return null;\n }\n\n return menuSchemaValue;\n });\n\n const handleClose = () => {\n if (activeMenu) {\n uiState.provides?.closeMenu(activeMenu.menuId);\n }\n };\n\n // Use the user-provided menu renderer\n const MenuRenderer = renderers.menu;\n</script>\n\n{#if activeMenu && menuSchema && MenuRenderer}\n <MenuRenderer\n schema={menuSchema}\n {documentId}\n anchorEl={activeMenu.anchorEl}\n onClose={handleClose}\n {container}\n />\n{/if}\n","<script lang=\"ts\">\n import { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\n import { useUIPlugin, useUICapability } from './hooks/use-ui.svelte';\n import { setUIContainerContext } from './hooks/use-ui-container.svelte';\n import type { Snippet } from 'svelte';\n import type { HTMLAttributes } from 'svelte/elements';\n\n type Props = HTMLAttributes<HTMLDivElement> & {\n children?: Snippet;\n };\n\n let { children, class: className, ...restProps }: Props = $props();\n\n const { plugin } = useUIPlugin();\n const { provides } = useUICapability();\n\n let disabledCategories = $state<string[]>([]);\n let hiddenItems = $state<string[]>([]);\n let rootElement: HTMLDivElement | null = $state(null);\n let styleEl: HTMLStyleElement | null = null;\n let styleTarget: HTMLElement | ShadowRoot | null = null;\n\n // Provide container context for child components\n setUIContainerContext({\n getContainer: () => rootElement,\n });\n\n function getStyleTarget(element: HTMLElement): HTMLElement | ShadowRoot {\n const root = element.getRootNode();\n if (root instanceof ShadowRoot) {\n return root;\n }\n return document.head;\n }\n\n $effect(() => {\n if (!rootElement || !plugin) {\n styleTarget = null;\n return;\n }\n\n styleTarget = getStyleTarget(rootElement);\n\n const existingStyle = styleTarget.querySelector(UI_SELECTORS.STYLES) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleEl = existingStyle;\n existingStyle.textContent = plugin.getStylesheet();\n return;\n }\n\n const stylesheet = plugin.getStylesheet();\n const newStyleEl = document.createElement('style');\n newStyleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n newStyleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n styleTarget.insertBefore(newStyleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(newStyleEl);\n }\n\n styleEl = newStyleEl;\n\n return () => {\n if (styleEl?.parentNode) {\n styleEl.remove();\n }\n styleEl = null;\n styleTarget = null;\n };\n });\n\n $effect(() => {\n if (!plugin) return;\n\n return plugin.onStylesheetInvalidated(() => {\n if (styleEl) {\n styleEl.textContent = plugin.getStylesheet();\n }\n });\n });\n\n $effect(() => {\n if (!provides) return;\n\n disabledCategories = provides.getDisabledCategories();\n hiddenItems = provides.getHiddenItems();\n\n return provides.onCategoryChanged((event) => {\n disabledCategories = event.disabledCategories;\n hiddenItems = event.hiddenItems;\n });\n });\n\n const disabledCategoriesAttr = $derived(\n disabledCategories.length > 0 ? disabledCategories.join(' ') : undefined,\n );\n\n const hiddenItemsAttr = $derived(hiddenItems.length > 0 ? hiddenItems.join(' ') : undefined);\n</script>\n\n<div\n bind:this={rootElement}\n {...restProps}\n {...{ [UI_ATTRIBUTES.ROOT]: '' }}\n {...disabledCategoriesAttr ? { [UI_ATTRIBUTES.DISABLED_CATEGORIES]: disabledCategoriesAttr } : {}}\n {...hiddenItemsAttr ? { [UI_ATTRIBUTES.HIDDEN_ITEMS]: hiddenItemsAttr } : {}}\n class={className}\n style:container-type=\"inline-size\"\n>\n {#if children}\n {@render children()}\n {/if}\n</div>\n","<script lang=\"ts\">\n import type { Component, Snippet } from 'svelte';\n import { provideAnchorRegistry } from './registries/anchor-registry.svelte';\n import { provideComponentRegistry } from './registries/component-registry.svelte';\n import { provideRenderers } from './registries/renderers-registry.svelte';\n import type { UIComponents, UIRenderers } from './types';\n import AutoMenuRenderer from './auto-menu-renderer.svelte';\n import UIRoot from './root.svelte';\n import type { HTMLAttributes } from 'svelte/elements';\n\n /**\n * UIProvider Props\n */\n type ProviderProps = HTMLAttributes<HTMLDivElement> & {\n children: Snippet;\n\n /**\n * Document ID for this UI context\n * Required for menu rendering\n */\n documentId: string;\n\n /**\n * Custom component registry\n * Maps component IDs to components\n */\n components?: UIComponents;\n\n /**\n * REQUIRED: User-provided renderers\n * These define how toolbars, panels, and menus are displayed\n */\n renderers: UIRenderers;\n\n /**\n * Optional: Container for menu portal\n * Defaults to document.body\n */\n menuContainer?: HTMLElement | null;\n\n class?: string;\n };\n\n let {\n children,\n documentId,\n components = {},\n renderers,\n menuContainer = null,\n class: className,\n ...restProps\n }: ProviderProps = $props();\n\n /**\n * UIProvider - Single provider for all UI plugin functionality\n *\n * Manages:\n * - Anchor registry for menu positioning\n * - Component registry for custom components\n * - Renderers for toolbars, panels, and menus\n * - Automatic menu rendering\n *\n * @example\n * ```svelte\n * <EmbedPDF {engine} {plugins}>\n * {#snippet children({ pluginsReady, activeDocumentId })}\n * {#if pluginsReady && activeDocumentId}\n * <UIProvider\n * documentId={activeDocumentId}\n * components={{\n * 'thumbnail-panel': ThumbnailPanel,\n * 'bookmark-panel': BookmarkPanel,\n * }}\n * renderers={{\n * toolbar: ToolbarRenderer,\n * panel: PanelRenderer,\n * menu: MenuRenderer,\n * }}\n * >\n * {#snippet children()}\n * <ViewerLayout />\n * {/snippet}\n * </UIProvider>\n * {/if}\n * {/snippet}\n * </EmbedPDF>\n * ```\n */\n\n // Provide all registries\n provideAnchorRegistry();\n provideComponentRegistry(components);\n provideRenderers(renderers);\n</script>\n\n<UIRoot class={className} {...restProps}>\n {@render children()}\n <AutoMenuRenderer {documentId} container={menuContainer} />\n</UIRoot>\n"],"names":["_a","$$anchor","root_1","UIRoot","AutoMenuRenderer"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/svelte/hooks/use-ui.svelte.ts","../../src/svelte/hooks/use-ui-container.svelte.ts","../../src/svelte/registries/anchor-registry.svelte.ts","../../src/svelte/hooks/use-register-anchor.svelte.ts","../../src/svelte/registries/component-registry.svelte.ts","../../src/svelte/hooks/use-item-renderer.svelte.ts","../../src/svelte/registries/renderers-registry.svelte.ts","../../src/svelte/hooks/use-schema-renderer.svelte.ts","../../src/svelte/hooks/use-selection-menu.svelte.ts","../../src/svelte/auto-menu-renderer.svelte","../../src/svelte/root.svelte","../../src/svelte/provider.svelte"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\nimport { UIPlugin, UIDocumentState, UIScope } from '@embedpdf/plugin-ui';\n\n/**\n * Hook to get the raw UI plugin instance.\n */\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\n\n/**\n * Hook to get the UI plugin's capability API.\n */\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\n\n// Define the return type explicitly to maintain type safety\ninterface UseUIStateReturn {\n provides: UIScope | null;\n state: UIDocumentState | null;\n}\n\n/**\n * Hook for UI state for a specific document\n * @param getDocumentId Function that returns the document ID\n */\nexport const useUIState = (getDocumentId: () => string | null): UseUIStateReturn => {\n const capability = useUICapability();\n\n let state = $state<UIDocumentState | null>(null);\n\n // Reactive documentId\n const documentId = $derived(getDocumentId());\n\n // Scoped capability for current docId\n const scopedProvides = $derived(\n capability.provides && documentId ? capability.provides.forDocument(documentId) : null,\n );\n\n $effect(() => {\n const provides = capability.provides;\n const docId = documentId;\n\n if (!provides || !docId) {\n state = null;\n return;\n }\n\n const scope = provides.forDocument(docId);\n\n // Set initial state\n state = scope.getState();\n\n // Subscribe to all changes and update state\n const unsubToolbar = scope.onToolbarChanged(() => {\n state = scope.getState();\n });\n const unsubSidebar = scope.onSidebarChanged(() => {\n state = scope.getState();\n });\n const unsubModal = scope.onModalChanged(() => {\n state = scope.getState();\n });\n const unsubMenu = scope.onMenuChanged(() => {\n state = scope.getState();\n });\n const unsubOverlay = scope.onOverlayChanged(() => {\n state = scope.getState();\n });\n\n return () => {\n unsubToolbar();\n unsubSidebar();\n unsubModal();\n unsubMenu();\n unsubOverlay();\n };\n });\n\n return {\n get provides() {\n return scopedProvides;\n },\n get state() {\n return state;\n },\n };\n};\n\n/**\n * Hook to get UI schema\n * Returns an object with a reactive getter for the schema\n */\nexport const useUISchema = () => {\n const capability = useUICapability();\n\n return {\n get schema() {\n return capability.provides?.getSchema() ?? null;\n },\n };\n};\n","import { getContext, setContext } from 'svelte';\n\nexport interface UIContainerContextValue {\n /** Get the container element (may be null if not mounted) */\n getContainer: () => HTMLDivElement | null;\n}\n\nconst UI_CONTAINER_KEY = Symbol('ui-container');\n\n/**\n * Set up the container context (called by UIRoot)\n */\nexport function setUIContainerContext(value: UIContainerContextValue): void {\n setContext(UI_CONTAINER_KEY, value);\n}\n\n/**\n * Hook to access the UI container element.\n *\n * This provides access to the UIRoot container for:\n * - Container query based responsiveness\n * - Portaling elements to the root\n * - Measuring container dimensions\n *\n * @example\n * ```svelte\n * <script>\n * import { useUIContainer } from '@embedpdf/plugin-ui/svelte';\n * import { onMount, onDestroy } from 'svelte';\n *\n * const { getContainer } = useUIContainer();\n *\n * let observer;\n *\n * onMount(() => {\n * const container = getContainer();\n * if (!container) return;\n *\n * observer = new ResizeObserver(() => {\n * console.log('Container width:', container.clientWidth);\n * });\n * observer.observe(container);\n * });\n *\n * onDestroy(() => observer?.disconnect());\n * </script>\n * ```\n */\nexport function useUIContainer(): UIContainerContextValue {\n const context = getContext<UIContainerContextValue>(UI_CONTAINER_KEY);\n if (!context) {\n throw new Error('useUIContainer must be used within a UIProvider');\n }\n return context;\n}\n","import { getContext, setContext } from 'svelte';\n\n/**\n * Anchor Registry\n *\n * Tracks DOM elements for menu positioning.\n * Each anchor is scoped by documentId and itemId.\n */\nexport interface AnchorRegistry {\n register(documentId: string, itemId: string, element: HTMLElement): void;\n unregister(documentId: string, itemId: string): void;\n getAnchor(documentId: string, itemId: string): HTMLElement | null;\n}\n\nconst ANCHOR_REGISTRY_KEY = Symbol('AnchorRegistry');\n\nexport function createAnchorRegistry(): AnchorRegistry {\n const anchors = new Map<string, HTMLElement>();\n\n return {\n register(documentId: string, itemId: string, element: HTMLElement) {\n const key = `${documentId}:${itemId}`;\n anchors.set(key, element);\n },\n\n unregister(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n anchors.delete(key);\n },\n\n getAnchor(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n return anchors.get(key) || null;\n },\n };\n}\n\nexport function provideAnchorRegistry() {\n const registry = createAnchorRegistry();\n setContext(ANCHOR_REGISTRY_KEY, registry);\n return registry;\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const registry = getContext<AnchorRegistry>(ANCHOR_REGISTRY_KEY);\n if (!registry) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { onDestroy } from 'svelte';\nimport { useAnchorRegistry } from '../registries/anchor-registry.svelte';\n\n/**\n * Register a DOM element as an anchor for menus\n *\n * @param getDocumentId - Function returning document ID\n * @param getItemId - Function returning item ID (typically matches the toolbar/menu item ID)\n * @returns Function to attach to the element via use:action\n *\n * @example\n *\n * <script lang=\"ts\">\n * const registerAnchor = useRegisterAnchor(() => documentId, () => 'zoom-button');\n * </script>\n *\n * <button use:registerAnchor>Zoom</button>\n * */\nexport function useRegisterAnchor(getDocumentId: () => string | null, getItemId: () => string) {\n const registry = useAnchorRegistry();\n let currentElement = $state<HTMLElement | null>(null);\n\n // Reactive values - these update when the functions return different values\n const documentId = $derived(getDocumentId());\n const itemId = $derived(getItemId());\n\n // Re-register anchor when documentId, itemId, or element changes\n $effect(() => {\n const docId = documentId;\n const item = itemId;\n const element = currentElement;\n\n // Only register if we have all required values\n if (element && docId && item) {\n registry.register(docId, item, element);\n\n // Cleanup: unregister when effect re-runs or component unmounts\n return () => {\n registry.unregister(docId, item);\n };\n }\n });\n\n // Svelte action function\n const action = (element: HTMLElement) => {\n currentElement = element;\n\n return {\n destroy() {\n // Clear the element reference when the action is destroyed\n currentElement = null;\n },\n };\n };\n\n return action;\n}\n","import { getContext, setContext, type Component } from 'svelte';\nimport type { BaseComponentProps } from '../types';\n\n/**\n * Component Registry\n *\n * Stores custom components that can be referenced in the UI schema.\n */\nexport interface ComponentRegistry {\n register(id: string, component: Component<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): Component<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst COMPONENT_REGISTRY_KEY = Symbol('ComponentRegistry');\n\nexport function createComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n): ComponentRegistry {\n const components = new Map<string, Component<BaseComponentProps>>(\n Object.entries(initialComponents),\n );\n\n return {\n register(id: string, component: Component<BaseComponentProps>) {\n components.set(id, component);\n },\n\n unregister(id: string) {\n components.delete(id);\n },\n\n get(id: string) {\n return components.get(id);\n },\n\n has(id: string) {\n return components.has(id);\n },\n\n getRegisteredIds() {\n return Array.from(components.keys());\n },\n };\n}\n\nexport function provideComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n) {\n const registry = createComponentRegistry(initialComponents);\n setContext(COMPONENT_REGISTRY_KEY, registry);\n return registry;\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const registry = getContext<ComponentRegistry>(COMPONENT_REGISTRY_KEY);\n if (!registry) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { useComponentRegistry } from '../registries/component-registry.svelte';\n\n/**\n * Helper utilities for building renderers\n */\nexport function useItemRenderer() {\n const componentRegistry = useComponentRegistry();\n\n return {\n /**\n * Get a custom component by ID\n *\n * @param componentId - Component ID from schema\n * @returns Component constructor or undefined if not found\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const { getCustomComponent } = useItemRenderer();\n * const MyComponent = getCustomComponent('my-component-id');\n * </script>\n *\n * {#if MyComponent}\n * <MyComponent {documentId} {...props} />\n * {/if}\n * ```\n */\n getCustomComponent: (componentId: string) => {\n const Component = componentRegistry.get(componentId);\n\n if (!Component) {\n console.error(`Component \"${componentId}\" not found in registry`);\n return undefined;\n }\n\n return Component;\n },\n };\n}\n","import { getContext, setContext } from 'svelte';\nimport type { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RENDERERS_KEY = Symbol('Renderers');\n\nexport function provideRenderers(renderers: UIRenderers) {\n setContext(RENDERERS_KEY, renderers);\n}\n\nexport function useRenderers(): UIRenderers {\n const renderers = getContext<UIRenderers>(RENDERERS_KEY);\n if (!renderers) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return renderers;\n}\n","import { useUICapability } from './use-ui.svelte';\nimport { useRenderers } from '../registries/renderers-registry.svelte';\n\n/**\n * High-level hook for rendering UI from schema\n *\n * Provides information about active toolbars, sidebars, and modals.\n * Always includes isOpen state so renderers can control animations.\n *\n * Use with Svelte's component binding to render toolbars and sidebars.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const { getToolbarInfo, getSidebarInfo, getModalInfo } = useSchemaRenderer(() => documentId);\n *\n * const topMainToolbar = $derived(getToolbarInfo('top', 'main'));\n * const leftMainSidebar = $derived(getSidebarInfo('left', 'main'));\n * const modal = $derived(getModalInfo());\n * </script>\n *\n * {#if topMainToolbar}\n * {@const ToolbarRenderer = topMainToolbar.renderer}\n * <ToolbarRenderer\n * schema={topMainToolbar.schema}\n * documentId={topMainToolbar.documentId}\n * isOpen={topMainToolbar.isOpen}\n * onClose={topMainToolbar.onClose}\n * />\n * {/if}\n * ```\n */\nexport function useSchemaRenderer(getDocumentId: () => string | null) {\n const renderers = useRenderers();\n const capability = useUICapability();\n const uiState = useUIState(getDocumentId);\n\n return {\n /**\n * Get toolbar information by placement and slot\n *\n * @param placement - 'top' | 'bottom' | 'left' | 'right'\n * @param slot - Slot name (e.g. 'main', 'secondary')\n * @returns Toolbar info or null if no toolbar in slot\n */\n getToolbarInfo: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string) => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema || !uiState.provides || !uiState.state || !documentId) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.state.activeToolbars[slotKey];\n\n // If no toolbar in this slot, nothing to render\n if (!toolbarSlot) return null;\n\n const toolbarSchema = schema.toolbars[toolbarSlot.toolbarId];\n if (!toolbarSchema) {\n console.warn(`Toolbar \"${toolbarSlot.toolbarId}\" not found in schema`);\n return null;\n }\n\n // Check if toolbar is closable\n const isClosable = !toolbarSchema.permanent;\n\n const handleClose = isClosable\n ? () => {\n uiState.provides?.closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n return {\n renderer: renderers.toolbar,\n schema: toolbarSchema,\n documentId,\n isOpen: toolbarSlot.isOpen,\n onClose: handleClose,\n };\n },\n\n /**\n * Get sidebar information by placement and slot\n *\n * @param placement - 'left' | 'right' | 'top' | 'bottom'\n * @param slot - Slot name (e.g. 'main', 'secondary', 'inspector')\n * @returns Sidebar info or null if no sidebar in slot\n */\n getSidebarInfo: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string) => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema || !uiState.provides || !uiState.state || !documentId) return null;\n\n const slotKey = `${placement}-${slot}`;\n const sidebarSlot = uiState.state.activeSidebars[slotKey];\n\n // If no sidebar in this slot, nothing to render\n if (!sidebarSlot) return null;\n\n const sidebarSchema = schema.sidebars?.[sidebarSlot.sidebarId];\n if (!sidebarSchema) {\n console.warn(`Sidebar \"${sidebarSlot.sidebarId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n uiState.provides?.closeSidebarSlot(placement, slot);\n };\n\n return {\n renderer: renderers.sidebar,\n schema: sidebarSchema,\n documentId,\n isOpen: sidebarSlot.isOpen,\n onClose: handleClose,\n };\n },\n\n /**\n * Get modal information (if active)\n *\n * Supports animation lifecycle:\n * - isOpen: true = visible\n * - isOpen: false = animate out (modal still rendered)\n * - onExited called after animation → modal removed\n *\n * @returns Modal info or null if no modal active\n */\n getModalInfo: () => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema || !uiState.provides || !uiState.state?.activeModal || !documentId) return null;\n\n const { modalId, isOpen } = uiState.state.activeModal;\n\n const modalSchema = schema.modals?.[modalId];\n if (!modalSchema) {\n console.warn(`Modal \"${modalId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n uiState.provides?.closeModal();\n };\n\n const handleExited = () => {\n uiState.provides?.clearModal();\n };\n\n const ModalRenderer = renderers.modal;\n if (!ModalRenderer) {\n console.warn('No modal renderer registered');\n return null;\n }\n\n return {\n renderer: ModalRenderer,\n schema: modalSchema,\n documentId,\n isOpen,\n onClose: handleClose,\n onExited: handleExited,\n };\n },\n\n /**\n * Helper: Get all active toolbars for this document\n * Useful for batch rendering or debugging\n */\n getActiveToolbars: () => {\n if (!uiState.state) return [];\n return Object.entries(uiState.state.activeToolbars).map(([slotKey, toolbarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n toolbarId: toolbarSlot.toolbarId,\n isOpen: toolbarSlot.isOpen,\n };\n });\n },\n\n /**\n * Helper: Get all active sidebars for this document\n * Useful for batch rendering or debugging\n */\n getActiveSidebars: () => {\n if (!uiState.state) return [];\n return Object.entries(uiState.state.activeSidebars).map(([slotKey, sidebarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n sidebarId: sidebarSlot.sidebarId,\n isOpen: sidebarSlot.isOpen,\n };\n });\n },\n\n /**\n * Get overlay information for all enabled overlays\n *\n * Overlays are floating components positioned over the document content.\n * Unlike modals, multiple overlays can be visible and they don't block interaction.\n * Overlay visibility is controlled by the enabledOverlays state.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const { getOverlaysInfo } = useSchemaRenderer(() => documentId);\n * const overlays = $derived(getOverlaysInfo());\n * </script>\n *\n * {#each overlays as overlay (overlay.schema.id)}\n * {@const OverlayRenderer = overlay.renderer}\n * <OverlayRenderer schema={overlay.schema} documentId={overlay.documentId} />\n * {/each}\n * ```\n */\n getOverlaysInfo: () => {\n const schema = capability.provides?.getSchema();\n const documentId = getDocumentId();\n\n if (!schema?.overlays || !documentId || !uiState.state) return [];\n\n const OverlayRenderer = renderers.overlay;\n if (!OverlayRenderer) {\n return [];\n }\n\n const overlays = Object.values(schema.overlays);\n\n // Filter overlays by enabled state (default to true if not explicitly set)\n const enabledOverlays = overlays.filter(\n (overlay) => uiState.state!.enabledOverlays[overlay.id] !== false,\n );\n\n return enabledOverlays.map((overlaySchema) => ({\n renderer: OverlayRenderer,\n schema: overlaySchema,\n documentId,\n }));\n },\n };\n}\n\n// Import after definition to avoid circular dependency\nimport { useUIState } from './use-ui.svelte';\n","import type {\n SelectionMenuPropsBase,\n SelectionMenuRenderFn,\n SelectionMenuRenderResult,\n} from '@embedpdf/utils/svelte';\nimport { useUICapability } from './use-ui.svelte';\nimport { useRenderers } from '../registries/renderers-registry.svelte';\n\n/**\n * Hook for schema-driven selection menus\n */\nexport function useSelectionMenu<TContext extends { type: string }>(\n menuId: string | (() => string),\n getDocumentId: () => string,\n) {\n const uiCapability = useUICapability();\n const renderers = useRenderers();\n\n // Normalize menuId to always be a function, then make it reactive\n const getMenuIdFn = typeof menuId === 'function' ? menuId : () => menuId;\n const menuIdValue = $derived(getMenuIdFn());\n const documentId = $derived(getDocumentId());\n const schema = $derived(uiCapability.provides?.getSchema());\n const menuSchema = $derived(schema?.selectionMenus?.[menuIdValue]);\n\n const renderFn = $derived.by<SelectionMenuRenderFn<TContext> | undefined>(() => {\n if (!menuSchema) return undefined;\n\n const currentMenuSchema = menuSchema;\n const currentDocumentId = documentId;\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n return (props: SelectionMenuPropsBase<TContext>): SelectionMenuRenderResult | null => {\n if (!props.selected) return null;\n\n return {\n component: SelectionMenuRenderer,\n props: {\n schema: currentMenuSchema,\n documentId: currentDocumentId,\n props,\n },\n };\n };\n });\n\n return {\n get renderFn() {\n return renderFn;\n },\n };\n}\n","<script lang=\"ts\">\n import { useUIState, useUICapability } from './hooks/use-ui.svelte';\n import { useAnchorRegistry } from './registries/anchor-registry.svelte';\n import { useRenderers } from './registries/renderers-registry.svelte';\n\n /**\n * Automatically renders menus when opened\n *\n * This component:\n * 1. Listens to UI plugin state for open menus\n * 2. Looks up anchor elements from the anchor registry\n * 3. Renders menus using the user-provided menu renderer\n */\n\n interface Props {\n documentId: string; // Which document's menus to render\n container?: HTMLElement | null;\n }\n\n let { documentId, container = null }: Props = $props();\n\n const uiState = useUIState(() => documentId);\n const capability = useUICapability();\n const anchorRegistry = useAnchorRegistry();\n const renderers = useRenderers();\n\n // Derived state for active menu\n const activeMenu = $derived.by(() => {\n const openMenus = uiState.state?.openMenus || {};\n const openMenuIds = Object.keys(openMenus);\n\n if (openMenuIds.length === 0) return null;\n\n // Show the first open menu (in practice, should only be one)\n const menuId = openMenuIds[0];\n if (!menuId) return null;\n\n const menuState = openMenus[menuId];\n if (!menuState || !menuState.triggeredByItemId) return null;\n\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(documentId, menuState.triggeredByItemId);\n return { menuId, anchorEl: anchor };\n });\n\n const schema = $derived(capability.provides?.getSchema());\n\n const menuSchema = $derived.by(() => {\n if (!activeMenu || !schema) return null;\n\n const menuSchemaValue = schema.menus[activeMenu.menuId];\n if (!menuSchemaValue) {\n console.warn(`Menu \"${activeMenu.menuId}\" not found in schema`);\n return null;\n }\n\n return menuSchemaValue;\n });\n\n const handleClose = () => {\n if (activeMenu) {\n uiState.provides?.closeMenu(activeMenu.menuId);\n }\n };\n\n // Use the user-provided menu renderer\n const MenuRenderer = renderers.menu;\n</script>\n\n{#if activeMenu && menuSchema && MenuRenderer}\n <MenuRenderer\n schema={menuSchema}\n {documentId}\n anchorEl={activeMenu.anchorEl}\n onClose={handleClose}\n {container}\n />\n{/if}\n","<script lang=\"ts\">\n import { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\n import { useUIPlugin, useUICapability } from './hooks/use-ui.svelte';\n import { setUIContainerContext } from './hooks/use-ui-container.svelte';\n import type { Snippet } from 'svelte';\n import type { HTMLAttributes } from 'svelte/elements';\n\n type Props = HTMLAttributes<HTMLDivElement> & {\n children?: Snippet;\n };\n\n let { children, class: className, ...restProps }: Props = $props();\n\n const { plugin } = useUIPlugin();\n const { provides } = useUICapability();\n\n let disabledCategories = $state<string[]>([]);\n let hiddenItems = $state<string[]>([]);\n let rootElement: HTMLDivElement | null = $state(null);\n let styleEl: HTMLStyleElement | null = null;\n let styleTarget: HTMLElement | ShadowRoot | null = null;\n\n // Provide container context for child components\n setUIContainerContext({\n getContainer: () => rootElement,\n });\n\n function getStyleTarget(element: HTMLElement): HTMLElement | ShadowRoot {\n const root = element.getRootNode();\n if (root instanceof ShadowRoot) {\n return root;\n }\n return document.head;\n }\n\n $effect(() => {\n if (!rootElement || !plugin) {\n styleTarget = null;\n return;\n }\n\n styleTarget = getStyleTarget(rootElement);\n\n const existingStyle = styleTarget.querySelector(UI_SELECTORS.STYLES) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleEl = existingStyle;\n existingStyle.textContent = plugin.getStylesheet();\n return;\n }\n\n const stylesheet = plugin.getStylesheet();\n const newStyleEl = document.createElement('style');\n newStyleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n newStyleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n styleTarget.insertBefore(newStyleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(newStyleEl);\n }\n\n styleEl = newStyleEl;\n\n return () => {\n if (styleEl?.parentNode) {\n styleEl.remove();\n }\n styleEl = null;\n styleTarget = null;\n };\n });\n\n $effect(() => {\n if (!plugin) return;\n\n return plugin.onStylesheetInvalidated(() => {\n if (styleEl) {\n styleEl.textContent = plugin.getStylesheet();\n }\n });\n });\n\n $effect(() => {\n if (!provides) return;\n\n disabledCategories = provides.getDisabledCategories();\n hiddenItems = provides.getHiddenItems();\n\n return provides.onCategoryChanged((event) => {\n disabledCategories = event.disabledCategories;\n hiddenItems = event.hiddenItems;\n });\n });\n\n const disabledCategoriesAttr = $derived(\n disabledCategories.length > 0 ? disabledCategories.join(' ') : undefined,\n );\n\n const hiddenItemsAttr = $derived(hiddenItems.length > 0 ? hiddenItems.join(' ') : undefined);\n</script>\n\n<div\n bind:this={rootElement}\n {...restProps}\n {...{ [UI_ATTRIBUTES.ROOT]: '' }}\n {...disabledCategoriesAttr ? { [UI_ATTRIBUTES.DISABLED_CATEGORIES]: disabledCategoriesAttr } : {}}\n {...hiddenItemsAttr ? { [UI_ATTRIBUTES.HIDDEN_ITEMS]: hiddenItemsAttr } : {}}\n class={className}\n style:container-type=\"inline-size\"\n>\n {#if children}\n {@render children()}\n {/if}\n</div>\n","<script lang=\"ts\">\n import type { Component, Snippet } from 'svelte';\n import { provideAnchorRegistry } from './registries/anchor-registry.svelte';\n import { provideComponentRegistry } from './registries/component-registry.svelte';\n import { provideRenderers } from './registries/renderers-registry.svelte';\n import type { UIComponents, UIRenderers } from './types';\n import AutoMenuRenderer from './auto-menu-renderer.svelte';\n import UIRoot from './root.svelte';\n import type { HTMLAttributes } from 'svelte/elements';\n\n /**\n * UIProvider Props\n */\n type ProviderProps = HTMLAttributes<HTMLDivElement> & {\n children: Snippet;\n\n /**\n * Document ID for this UI context\n * Required for menu rendering\n */\n documentId: string;\n\n /**\n * Custom component registry\n * Maps component IDs to components\n */\n components?: UIComponents;\n\n /**\n * REQUIRED: User-provided renderers\n * These define how toolbars, panels, and menus are displayed\n */\n renderers: UIRenderers;\n\n /**\n * Optional: Container for menu portal\n * Defaults to document.body\n */\n menuContainer?: HTMLElement | null;\n\n class?: string;\n };\n\n let {\n children,\n documentId,\n components = {},\n renderers,\n menuContainer = null,\n class: className,\n ...restProps\n }: ProviderProps = $props();\n\n /**\n * UIProvider - Single provider for all UI plugin functionality\n *\n * Manages:\n * - Anchor registry for menu positioning\n * - Component registry for custom components\n * - Renderers for toolbars, panels, and menus\n * - Automatic menu rendering\n *\n * @example\n * ```svelte\n * <EmbedPDF {engine} {plugins}>\n * {#snippet children({ pluginsReady, activeDocumentId })}\n * {#if pluginsReady && activeDocumentId}\n * <UIProvider\n * documentId={activeDocumentId}\n * components={{\n * 'thumbnail-panel': ThumbnailPanel,\n * 'bookmark-panel': BookmarkPanel,\n * }}\n * renderers={{\n * toolbar: ToolbarRenderer,\n * panel: PanelRenderer,\n * menu: MenuRenderer,\n * }}\n * >\n * {#snippet children()}\n * <ViewerLayout />\n * {/snippet}\n * </UIProvider>\n * {/if}\n * {/snippet}\n * </EmbedPDF>\n * ```\n */\n\n // Provide all registries\n provideAnchorRegistry();\n provideComponentRegistry(components);\n provideRenderers(renderers);\n</script>\n\n<UIRoot class={className} {...restProps}>\n {@render children()}\n <AutoMenuRenderer {documentId} container={menuContainer} />\n</UIRoot>\n"],"names":["_a","$$anchor","root_1","UIRoot","AutoMenuRenderer"],"mappings":"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"}
|
|
@@ -82,6 +82,7 @@ export declare function useSchemaRenderer(documentId: MaybeRefOrGetter<string>):
|
|
|
82
82
|
*
|
|
83
83
|
* Overlays are floating components positioned over the document content.
|
|
84
84
|
* Unlike modals, multiple overlays can be visible and they don't block interaction.
|
|
85
|
+
* Overlay visibility is controlled by the enabledOverlays state.
|
|
85
86
|
*
|
|
86
87
|
* @example
|
|
87
88
|
* ```vue
|
|
@@ -35,6 +35,9 @@ export declare const useUIState: (documentId: MaybeRefOrGetter<string>) => {
|
|
|
35
35
|
readonly sidebarTabs: {
|
|
36
36
|
readonly [x: string]: string;
|
|
37
37
|
};
|
|
38
|
+
readonly enabledOverlays: {
|
|
39
|
+
readonly [x: string]: boolean;
|
|
40
|
+
};
|
|
38
41
|
} | null, {
|
|
39
42
|
readonly activeToolbars: {
|
|
40
43
|
readonly [x: string]: {
|
|
@@ -62,6 +65,9 @@ export declare const useUIState: (documentId: MaybeRefOrGetter<string>) => {
|
|
|
62
65
|
readonly sidebarTabs: {
|
|
63
66
|
readonly [x: string]: string;
|
|
64
67
|
};
|
|
68
|
+
readonly enabledOverlays: {
|
|
69
|
+
readonly [x: string]: boolean;
|
|
70
|
+
};
|
|
65
71
|
} | null>>;
|
|
66
72
|
};
|
|
67
73
|
/**
|
package/dist/vue/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),n=require("@embedpdf/core/vue"),t=require("@embedpdf/plugin-ui"),o=()=>n.usePlugin(t.UIPlugin.id),r=()=>n.useCapability(t.UIPlugin.id),u=n=>{const{provides:t}=r(),o=e.ref(null);e.watch([t,()=>e.toValue(n)],([e,n],t,r)=>{if(!e)return void(o.value=null);const u=e.forDocument(n);o.value=u.getState();const l=u.onToolbarChanged(()=>{o.value=u.getState()}),a=u.onSidebarChanged(()=>{o.value=u.getState()}),s=u.onModalChanged(()=>{o.value=u.getState()}),i=u.onMenuChanged(()=>{o.value=u.getState()});r(()=>{l(),a(),s(),i()})},{immediate:!0});return{provides:e.computed(()=>{var o;const r=e.toValue(n);return(null==(o=t.value)?void 0:o.forDocument(r))??null}),state:e.readonly(o)}},l=Symbol("ui-container");const a=Symbol("AnchorRegistry");function s(){const n=e.ref(new Map);return{register(e,t,o){const r=`${e}:${t}`;n.value.set(r,o)},unregister(e,t){const o=`${e}:${t}`;n.value.delete(o)},getAnchor(e,t){const o=`${e}:${t}`;return n.value.get(o)||null}}}function i(){const n=s();return e.provide(a,n),n}function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),n=require("@embedpdf/core/vue"),t=require("@embedpdf/plugin-ui"),o=()=>n.usePlugin(t.UIPlugin.id),r=()=>n.useCapability(t.UIPlugin.id),u=n=>{const{provides:t}=r(),o=e.ref(null);e.watch([t,()=>e.toValue(n)],([e,n],t,r)=>{if(!e)return void(o.value=null);const u=e.forDocument(n);o.value=u.getState();const l=u.onToolbarChanged(()=>{o.value=u.getState()}),a=u.onSidebarChanged(()=>{o.value=u.getState()}),s=u.onModalChanged(()=>{o.value=u.getState()}),i=u.onMenuChanged(()=>{o.value=u.getState()}),d=u.onOverlayChanged(()=>{o.value=u.getState()});r(()=>{l(),a(),s(),i(),d()})},{immediate:!0});return{provides:e.computed(()=>{var o;const r=e.toValue(n);return(null==(o=t.value)?void 0:o.forDocument(r))??null}),state:e.readonly(o)}},l=Symbol("ui-container");const a=Symbol("AnchorRegistry");function s(){const n=e.ref(new Map);return{register(e,t,o){const r=`${e}:${t}`;n.value.set(r,o)},unregister(e,t){const o=`${e}:${t}`;n.value.delete(o)},getAnchor(e,t){const o=`${e}:${t}`;return n.value.get(o)||null}}}function i(){const n=s();return e.provide(a,n),n}function d(){const n=e.inject(a);if(!n)throw new Error("useAnchorRegistry must be used within UIProvider");return n}const c=Symbol("ComponentRegistry");function v(n={}){const t=e.ref(new Map(Object.entries(n)));return{register(e,n){t.value.set(e,n)},unregister(e){t.value.delete(e)},get:e=>t.value.get(e),has:e=>t.value.has(e),getRegisteredIds:()=>Array.from(t.value.keys())}}function m(n={}){const t=v(n);return e.provide(c,t),t}function p(){const n=e.inject(c);if(!n)throw new Error("useComponentRegistry must be used within UIProvider");return n}const f=Symbol("Renderers");function g(n){e.provide(f,n)}function h(){const n=e.inject(f);if(!n)throw new Error("useRenderers must be used within UIProvider");return n}const I=e.defineComponent({__name:"auto-menu-renderer",props:{documentId:{},container:{}},setup(n){const t=n,{state:o}=u(t.documentId),{provides:l}=r(),a=d(),s=h(),i=e.ref(null),c=e.computed(()=>{var e;return(null==(e=o.value)?void 0:e.openMenus)||{}}),v=e.computed(()=>{var e;return null==(e=l.value)?void 0:e.getSchema()});e.watch(c,e=>{const n=Object.keys(e);if(n.length>0){const o=n[0];if(!o)return void(i.value=null);const r=e[o];if(r&&r.triggeredByItemId){const e=a.getAnchor(t.documentId,r.triggeredByItemId);i.value={menuId:o,anchorEl:e}}else i.value=null}else i.value=null},{immediate:!0});const m=e.computed(()=>{if(!i.value||!v.value)return null;const e=v.value.menus[i.value.menuId];return e||(console.warn(`Menu "${i.value.menuId}" not found in schema`),null)}),p=()=>{var e;i.value&&(null==(e=l.value)||e.forDocument(t.documentId).closeMenu(i.value.menuId))},f=e.computed(()=>s.menu);return(t,o)=>i.value&&m.value&&f.value?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(f.value),{key:0,schema:m.value,documentId:n.documentId,anchorEl:i.value.anchorEl,onClose:p,container:n.container},null,40,["schema","documentId","anchorEl","container"])):e.createCommentVNode("",!0)}}),b=e.defineComponent({inheritAttrs:!1,__name:"root",setup(n){const u=e.useAttrs(),{plugin:a}=o(),{provides:s}=r(),i=e.ref([]),d=e.ref([]),c=e.ref(null),v={containerRef:c,getContainer:()=>c.value};e.provide(l,v);let m=null,p=null;function f(){if(!c.value||!a.value)return;p=function(e){const n=e.getRootNode();return n instanceof ShadowRoot?n:document.head}(c.value);const e=p.querySelector(t.UI_SELECTORS.STYLES);if(e)return m=e,void(e.textContent=a.value.getStylesheet());const n=a.value.getStylesheet(),o=document.createElement("style");o.setAttribute(t.UI_ATTRIBUTES.STYLES,""),o.textContent=n,p instanceof ShadowRoot?p.insertBefore(o,p.firstChild):p.appendChild(o),m=o}const g=e.computed(()=>{const e={[t.UI_ATTRIBUTES.ROOT]:""};return i.value.length>0&&(e[t.UI_ATTRIBUTES.DISABLED_CATEGORIES]=i.value.join(" ")),d.value.length>0&&(e[t.UI_ATTRIBUTES.HIDDEN_ITEMS]=d.value.join(" ")),e});let h=null,I=null;return e.onMounted(()=>{f(),a.value&&(h=a.value.onStylesheetInvalidated(()=>{m&&a.value&&(m.textContent=a.value.getStylesheet())})),s.value&&(i.value=s.value.getDisabledCategories(),d.value=s.value.getHiddenItems(),I=s.value.onCategoryChanged(e=>{i.value=e.disabledCategories,d.value=e.hiddenItems}))}),e.onUnmounted(()=>{(null==m?void 0:m.parentNode)&&m.remove(),m=null,p=null,null==h||h(),null==I||I()}),e.watch(a,()=>{c.value&&a.value&&f()}),(n,t)=>(e.openBlock(),e.createElementBlock("div",e.mergeProps({ref_key:"rootRef",ref:c},{...e.unref(u),...g.value},{style:{containerType:"inline-size"}}),[e.renderSlot(n.$slots,"default")],16))}}),S=e.defineComponent({inheritAttrs:!1,__name:"provider",props:{documentId:{},components:{default:()=>({})},renderers:{},menuContainer:{default:null}},setup(n){const t=e.useAttrs(),o=n;return i(),m(o.components),g(o.renderers),(o,r)=>(e.openBlock(),e.createBlock(b,e.normalizeProps(e.guardReactiveProps(e.unref(t))),{default:e.withCtx(()=>[e.renderSlot(o.$slots,"default"),e.createVNode(I,{documentId:n.documentId,container:n.menuContainer},null,8,["documentId","container"])]),_:3},16))}});exports.AutoMenuRenderer=I,exports.UIProvider=S,exports.UI_CONTAINER_KEY=l,exports.createAnchorRegistry=s,exports.createComponentRegistry=v,exports.provideAnchorRegistry=i,exports.provideComponentRegistry=m,exports.provideRenderers=g,exports.useAnchorRegistry=d,exports.useComponentRegistry=p,exports.useItemRenderer=function(){const n=p();return{renderCustomComponent:(t,o,r)=>{const u=n.get(t);return u?e.h(u,{documentId:o,...r||{}}):(console.error(`Component "${t}" not found in registry`),null)}}},exports.useRegisterAnchor=function(n,t){const o=d(),r=e.ref(null);return e.onBeforeUnmount(()=>{r.value&&o.unregister(n,t)}),e=>{const u=(null==e?void 0:e.$el)||e;r.value&&r.value!==u&&o.unregister(n,t),r.value=u,u&&o.register(n,t,u)}},exports.useRenderers=h,exports.useSchemaRenderer=function(n){const t=h(),{provides:o}=r(),{state:l}=u(n);return{renderToolbar:(r,u)=>{var a;const s=null==(a=o.value)?void 0:a.getSchema();if(!s||!o.value||!l.value)return null;const i=`${r}-${u}`,d=l.value.activeToolbars[i];if(!d)return null;const c=s.toolbars[d.toolbarId];if(!c)return console.warn(`Toolbar "${d.toolbarId}" not found in schema`),null;const v=!c.permanent?()=>{var t;null==(t=o.value)||t.forDocument(e.toValue(n)).closeToolbarSlot(r,u)}:void 0,m=t.toolbar;return e.h(m,{key:d.toolbarId,schema:c,documentId:e.toValue(n),isOpen:d.isOpen,onClose:v})},renderSidebar:(r,u)=>{var a,s;const i=null==(a=o.value)?void 0:a.getSchema();if(!i||!o.value||!l.value)return null;const d=`${r}-${u}`,c=l.value.activeSidebars[d];if(!c)return null;const v=null==(s=i.sidebars)?void 0:s[c.sidebarId];if(!v)return console.warn(`Sidebar "${c.sidebarId}" not found in schema`),null;const m=t.sidebar;return e.h(m,{key:c.sidebarId,schema:v,documentId:e.toValue(n),isOpen:c.isOpen,onClose:()=>{var t;null==(t=o.value)||t.forDocument(e.toValue(n)).closeSidebarSlot(r,u)}})},renderModal:()=>{var r,u,a;const s=null==(r=o.value)?void 0:r.getSchema();if(!s||!o.value||!(null==(u=l.value)?void 0:u.activeModal))return null;const{modalId:i,isOpen:d}=l.value.activeModal,c=null==(a=s.modals)?void 0:a[i];if(!c)return console.warn(`Modal "${i}" not found in schema`),null;const v=t.modal;return v?e.h(v,{key:i,schema:c,documentId:e.toValue(n),isOpen:d,onClose:()=>{var t;null==(t=o.value)||t.forDocument(e.toValue(n)).closeModal()},onExited:()=>{var t;null==(t=o.value)||t.forDocument(e.toValue(n)).clearModal()}}):(console.warn("No modal renderer registered"),null)},getActiveToolbars:()=>l.value?Object.entries(l.value.activeToolbars).map(([e,n])=>{const[t,o]=e.split("-");return{placement:t,slot:o,toolbarId:n.toolbarId,isOpen:n.isOpen}}):[],getActiveSidebars:()=>l.value?Object.entries(l.value.activeSidebars).map(([e,n])=>{const[t,o]=e.split("-");return{placement:t,slot:o,sidebarId:n.sidebarId,isOpen:n.isOpen}}):[],renderOverlays:()=>{var r;const u=null==(r=o.value)?void 0:r.getSchema();if(!(null==u?void 0:u.overlays)||!o.value||!l.value)return null;const a=t.overlay;if(!a)return null;const s=Object.values(u.overlays);if(0===s.length)return null;return s.filter(e=>!1!==l.value.enabledOverlays[e.id]).map(t=>e.h(a,{key:t.id,schema:t,documentId:e.toValue(n)}))}}},exports.useSelectionMenu=function(n,t){const{provides:o}=r(),u=h(),l=e.computed(()=>{var e;return null==(e=o.value)?void 0:e.getSchema()}),a=e.computed(()=>{var t,o;return null==(o=null==(t=l.value)?void 0:t.selectionMenus)?void 0:o[e.toValue(n)]});return e.computed(()=>{if(!a.value)return;const n=a.value,o=e.toValue(t),r=u.selectionMenu;return t=>t.selected?e.h(r,{schema:n,documentId:o,props:t}):null})},exports.useUICapability=r,exports.useUIContainer=function(){const n=e.inject(l);if(!n)throw new Error("useUIContainer must be used within a UIProvider");return n},exports.useUIPlugin=o,exports.useUISchema=()=>{const{provides:n}=r(),t=e.computed(()=>{var e;return(null==(e=n.value)?void 0:e.getSchema())??null});return e.readonly(t)},exports.useUIState=u,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/vue/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/vue/hooks/use-ui.ts","../../src/vue/hooks/use-ui-container.ts","../../src/vue/registries/anchor-registry.ts","../../src/vue/registries/component-registry.ts","../../src/vue/registries/renderers-registry.ts","../../src/vue/auto-menu-renderer.vue","../../src/vue/root.vue","../../src/vue/provider.vue","../../src/vue/hooks/use-item-renderer.ts","../../src/vue/hooks/use-register-anchor.ts","../../src/vue/hooks/use-schema-renderer.ts","../../src/vue/hooks/use-selection-menu.ts"],"sourcesContent":["import { ref, watch, computed, readonly, toValue, type MaybeRefOrGetter } from 'vue';\nimport { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { UIPlugin, UIDocumentState, UISchema } from '@embedpdf/plugin-ui';\n\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\n\n/**\n * Hook for UI state for a specific document\n * @param documentId Document ID (can be ref, computed, getter, or plain value)\n */\nexport const useUIState = (documentId: MaybeRefOrGetter<string>) => {\n const { provides } = useUICapability();\n const state = ref<UIDocumentState | null>(null);\n\n watch(\n [provides, () => toValue(documentId)],\n ([providesValue, docId], _, onCleanup) => {\n if (!providesValue) {\n state.value = null;\n return;\n }\n\n const scope = providesValue.forDocument(docId);\n\n // Set initial state\n state.value = scope.getState();\n\n // Subscribe to all changes\n const unsubToolbar = scope.onToolbarChanged(() => {\n state.value = scope.getState();\n });\n const unsubSidebar = scope.onSidebarChanged(() => {\n state.value = scope.getState();\n });\n const unsubModal = scope.onModalChanged(() => {\n state.value = scope.getState();\n });\n const unsubMenu = scope.onMenuChanged(() => {\n state.value = scope.getState();\n });\n\n onCleanup(() => {\n unsubToolbar();\n unsubSidebar();\n unsubModal();\n unsubMenu();\n });\n },\n { immediate: true },\n );\n\n // Return a computed ref for the scoped capability\n const scopedProvides = computed(() => {\n const docId = toValue(documentId);\n return provides.value?.forDocument(docId) ?? null;\n });\n\n return {\n provides: scopedProvides,\n state: readonly(state),\n };\n};\n\n/**\n * Hook to get UI schema\n */\nexport const useUISchema = () => {\n const { provides } = useUICapability();\n const schema = computed<UISchema | null>(() => provides.value?.getSchema() ?? null);\n\n return readonly(schema);\n};\n","import { inject, type Ref, type InjectionKey } from 'vue';\n\nexport interface UIContainerContextValue {\n /** Reference to the UIRoot container element */\n containerRef: Ref<HTMLDivElement | null>;\n /** Get the container element (may be null if not mounted) */\n getContainer: () => HTMLDivElement | null;\n}\n\nexport const UI_CONTAINER_KEY: InjectionKey<UIContainerContextValue> = Symbol('ui-container');\n\n/**\n * Hook to access the UI container element.\n *\n * This provides access to the UIRoot container for:\n * - Container query based responsiveness\n * - Portaling elements to the root\n * - Measuring container dimensions\n *\n * @example\n * ```vue\n * <script setup>\n * import { useUIContainer } from '@embedpdf/plugin-ui/vue';\n * import { onMounted, onUnmounted } from 'vue';\n *\n * const { containerRef, getContainer } = useUIContainer();\n *\n * onMounted(() => {\n * const container = getContainer();\n * if (!container) return;\n *\n * const observer = new ResizeObserver(() => {\n * console.log('Container width:', container.clientWidth);\n * });\n * observer.observe(container);\n *\n * onUnmounted(() => observer.disconnect());\n * });\n * </script>\n * ```\n */\nexport function useUIContainer(): UIContainerContextValue {\n const context = inject(UI_CONTAINER_KEY);\n if (!context) {\n throw new Error('useUIContainer must be used within a UIProvider');\n }\n return context;\n}\n","import { ref, inject, provide, type InjectionKey, type Ref } from 'vue';\n\n/**\n * Anchor Registry\n *\n * Tracks DOM elements for menu positioning.\n * Each anchor is scoped by documentId and itemId.\n */\nexport interface AnchorRegistry {\n register(documentId: string, itemId: string, element: HTMLElement): void;\n unregister(documentId: string, itemId: string): void;\n getAnchor(documentId: string, itemId: string): HTMLElement | null;\n}\n\nconst AnchorRegistryKey: InjectionKey<AnchorRegistry> = Symbol('AnchorRegistry');\n\nexport function createAnchorRegistry(): AnchorRegistry {\n const anchors: Ref<Map<string, HTMLElement>> = ref(new Map());\n\n return {\n register(documentId: string, itemId: string, element: HTMLElement) {\n const key = `${documentId}:${itemId}`;\n anchors.value.set(key, element);\n },\n\n unregister(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n anchors.value.delete(key);\n },\n\n getAnchor(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n return anchors.value.get(key) || null;\n },\n };\n}\n\nexport function provideAnchorRegistry() {\n const registry = createAnchorRegistry();\n provide(AnchorRegistryKey, registry);\n return registry;\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const registry = inject(AnchorRegistryKey);\n if (!registry) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { ref, inject, provide, type Component, type InjectionKey, type Ref } from 'vue';\nimport type { BaseComponentProps } from '../types';\n\n/**\n * Component Registry\n *\n * Stores custom components that can be referenced in the UI schema.\n */\nexport interface ComponentRegistry {\n register(id: string, component: Component<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): Component<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst ComponentRegistryKey: InjectionKey<ComponentRegistry> = Symbol('ComponentRegistry');\n\nexport function createComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n): ComponentRegistry {\n const components: Ref<Map<string, Component<BaseComponentProps>>> = ref(\n new Map(Object.entries(initialComponents)),\n );\n\n return {\n register(id: string, component: Component<BaseComponentProps>) {\n components.value.set(id, component);\n },\n\n unregister(id: string) {\n components.value.delete(id);\n },\n\n get(id: string) {\n return components.value.get(id);\n },\n\n has(id: string) {\n return components.value.has(id);\n },\n\n getRegisteredIds() {\n return Array.from(components.value.keys());\n },\n };\n}\n\nexport function provideComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n) {\n const registry = createComponentRegistry(initialComponents);\n provide(ComponentRegistryKey, registry);\n return registry;\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const registry = inject(ComponentRegistryKey);\n if (!registry) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { inject, provide, type InjectionKey } from 'vue';\nimport type { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RenderersKey: InjectionKey<UIRenderers> = Symbol('Renderers');\n\nexport function provideRenderers(renderers: UIRenderers) {\n provide(RenderersKey, renderers);\n}\n\nexport function useRenderers(): UIRenderers {\n const renderers = inject(RenderersKey);\n if (!renderers) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return renderers;\n}\n","<template>\n <component\n v-if=\"activeMenu && menuSchema && MenuRenderer\"\n :is=\"MenuRenderer\"\n :schema=\"menuSchema\"\n :documentId=\"documentId\"\n :anchorEl=\"activeMenu.anchorEl\"\n :onClose=\"handleClose\"\n :container=\"container\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport { useUIState, useUICapability } from './hooks/use-ui';\nimport { useAnchorRegistry } from './registries/anchor-registry';\nimport { useRenderers } from './registries/renderers-registry';\n\n/**\n * Automatically renders menus when opened\n *\n * This component:\n * 1. Listens to UI plugin state for open menus\n * 2. Looks up anchor elements from the anchor registry\n * 3. Renders menus using the user-provided menu renderer\n */\n\ninterface Props {\n documentId: string; // Which document's menus to render\n container?: HTMLElement | null;\n}\n\nconst props = defineProps<Props>();\n\nconst { state: uiState } = useUIState(props.documentId);\nconst { provides } = useUICapability();\nconst anchorRegistry = useAnchorRegistry();\nconst renderers = useRenderers();\n\nconst activeMenu = ref<{\n menuId: string;\n anchorEl: HTMLElement | null;\n} | null>(null);\n\nconst openMenus = computed(() => uiState.value?.openMenus || {});\nconst schema = computed(() => provides.value?.getSchema());\n\n// Update active menu when state changes\nwatch(\n openMenus,\n (menus) => {\n const openMenuIds = Object.keys(menus);\n\n if (openMenuIds.length > 0) {\n // Show the first open menu (in practice, should only be one)\n const menuId = openMenuIds[0];\n if (!menuId) {\n activeMenu.value = null;\n return;\n }\n\n const menuState = menus[menuId];\n if (menuState && menuState.triggeredByItemId) {\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(props.documentId, menuState.triggeredByItemId);\n activeMenu.value = { menuId, anchorEl: anchor };\n } else {\n activeMenu.value = null;\n }\n } else {\n activeMenu.value = null;\n }\n },\n { immediate: true },\n);\n\nconst menuSchema = computed(() => {\n if (!activeMenu.value || !schema.value) return null;\n\n const menuSchemaValue = schema.value.menus[activeMenu.value.menuId];\n if (!menuSchemaValue) {\n console.warn(`Menu \"${activeMenu.value.menuId}\" not found in schema`);\n return null;\n }\n\n return menuSchemaValue;\n});\n\nconst handleClose = () => {\n if (activeMenu.value) {\n provides.value?.forDocument(props.documentId).closeMenu(activeMenu.value.menuId);\n }\n};\n\n// Use the user-provided menu renderer\nconst MenuRenderer = computed(() => renderers.menu);\n</script>\n","<template>\n <div\n ref=\"rootRef\"\n v-bind=\"{ ...attrs, ...(rootAttrs as any) }\"\n :style=\"{ containerType: 'inline-size' }\"\n >\n <slot />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, watch, useAttrs, provide } from 'vue';\nimport { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\nimport { useUIPlugin, useUICapability } from './hooks/use-ui';\nimport { UI_CONTAINER_KEY, type UIContainerContextValue } from './hooks/use-ui-container';\n\n// Disable automatic attribute inheritance since we handle it manually\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst attrs = useAttrs();\n\nconst { plugin } = useUIPlugin();\nconst { provides } = useUICapability();\n\nconst disabledCategories = ref<string[]>([]);\nconst hiddenItems = ref<string[]>([]);\nconst rootRef = ref<HTMLDivElement | null>(null);\n\n// Provide container context for child components\nconst containerContext: UIContainerContextValue = {\n containerRef: rootRef,\n getContainer: () => rootRef.value,\n};\nprovide(UI_CONTAINER_KEY, containerContext);\n\nlet styleEl: HTMLStyleElement | null = null;\nlet styleTarget: HTMLElement | ShadowRoot | null = null;\n\n/**\n * Find the style injection target for an element.\n * Returns the shadow root if inside one, otherwise document.head.\n */\nfunction getStyleTarget(element: HTMLElement): HTMLElement | ShadowRoot {\n const root = element.getRootNode();\n if (root instanceof ShadowRoot) {\n return root;\n }\n return document.head;\n}\n\n/**\n * Inject or update stylesheet\n */\nfunction injectStyles() {\n if (!rootRef.value || !plugin.value) {\n return;\n }\n\n styleTarget = getStyleTarget(rootRef.value);\n\n // Check if styles already exist in this target\n const existingStyle = styleTarget.querySelector(UI_SELECTORS.STYLES) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleEl = existingStyle;\n // Update content in case locale changed\n existingStyle.textContent = plugin.value.getStylesheet();\n return;\n }\n\n // Create and inject stylesheet\n const stylesheet = plugin.value.getStylesheet();\n const newStyleEl = document.createElement('style');\n newStyleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n newStyleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n styleTarget.insertBefore(newStyleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(newStyleEl);\n }\n\n styleEl = newStyleEl;\n}\n\n/**\n * Cleanup styles\n */\nfunction cleanupStyles() {\n if (styleEl?.parentNode) {\n styleEl.remove();\n }\n styleEl = null;\n styleTarget = null;\n}\n\n// Build root element attributes\nconst rootAttrs = computed(() => {\n const result: Record<string, string> = {\n [UI_ATTRIBUTES.ROOT]: '',\n };\n\n if (disabledCategories.value.length > 0) {\n result[UI_ATTRIBUTES.DISABLED_CATEGORIES] = disabledCategories.value.join(' ');\n }\n\n if (hiddenItems.value.length > 0) {\n result[UI_ATTRIBUTES.HIDDEN_ITEMS] = hiddenItems.value.join(' ');\n }\n\n return result;\n});\n\n// Stylesheet invalidation cleanup\nlet stylesheetCleanup: (() => void) | null = null;\n\n// Category change cleanup\nlet categoryCleanup: (() => void) | null = null;\n\nonMounted(() => {\n // Inject styles on mount\n injectStyles();\n\n // Subscribe to stylesheet invalidation\n if (plugin.value) {\n stylesheetCleanup = plugin.value.onStylesheetInvalidated(() => {\n if (styleEl && plugin.value) {\n styleEl.textContent = plugin.value.getStylesheet();\n }\n });\n }\n\n // Subscribe to category changes\n if (provides.value) {\n disabledCategories.value = provides.value.getDisabledCategories();\n hiddenItems.value = provides.value.getHiddenItems();\n\n categoryCleanup = provides.value.onCategoryChanged((event) => {\n disabledCategories.value = event.disabledCategories;\n hiddenItems.value = event.hiddenItems;\n });\n }\n});\n\nonUnmounted(() => {\n cleanupStyles();\n stylesheetCleanup?.();\n categoryCleanup?.();\n});\n\n// Re-inject styles if plugin changes\nwatch(plugin, () => {\n if (rootRef.value && plugin.value) {\n injectStyles();\n }\n});\n</script>\n","<template>\n <UIRoot v-bind=\"attrs\">\n <slot />\n <!-- Automatically render menus for this document -->\n <AutoMenuRenderer :documentId=\"documentId\" :container=\"menuContainer\" />\n </UIRoot>\n</template>\n\n<script setup lang=\"ts\">\nimport type { Component } from 'vue';\nimport { useAttrs } from 'vue';\nimport { provideAnchorRegistry } from './registries/anchor-registry';\nimport { provideComponentRegistry } from './registries/component-registry';\nimport { provideRenderers } from './registries/renderers-registry';\nimport type { BaseComponentProps, UIRenderers } from './types';\nimport AutoMenuRenderer from './auto-menu-renderer.vue';\nimport UIRoot from './root.vue';\n\n// Disable automatic attribute inheritance since we pass them to UIRoot\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst attrs = useAttrs();\n\n/**\n * UIProvider Props\n */\ninterface Props {\n /**\n * Document ID for this UI context\n * Required for menu rendering\n */\n documentId: string;\n\n /**\n * Custom component registry\n * Maps component IDs to components\n */\n components?: Record<string, Component<BaseComponentProps>>;\n\n /**\n * REQUIRED: User-provided renderers\n * These define how toolbars, panels, and menus are displayed\n */\n renderers: UIRenderers;\n\n /**\n * Optional: Container for menu portal\n * Defaults to document.body\n */\n menuContainer?: HTMLElement | null;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n components: () => ({}),\n menuContainer: null,\n});\n\n/**\n * UIProvider - Single provider for all UI plugin functionality\n *\n * Manages:\n * - Anchor registry for menu positioning\n * - Component registry for custom components\n * - Renderers for toolbars, panels, and menus\n * - Automatic menu rendering\n *\n * @example\n * ```vue\n * <EmbedPDF :engine=\"engine\" :plugins=\"plugins\">\n * <template v-slot=\"{ pluginsReady, activeDocumentId }\">\n * <UIProvider\n * v-if=\"pluginsReady && activeDocumentId\"\n * :documentId=\"activeDocumentId\"\n * :components=\"{\n * 'thumbnail-panel': ThumbnailPanel,\n * 'bookmark-panel': BookmarkPanel,\n * }\"\n * :renderers=\"{\n * toolbar: ToolbarRenderer,\n * panel: PanelRenderer,\n * menu: MenuRenderer,\n * }\"\n * class=\"relative flex h-full w-full\"\n * >\n * <ViewerLayout />\n * </UIProvider>\n * </template>\n * </EmbedPDF>\n * ```\n */\n\n// Provide all registries\nprovideAnchorRegistry();\nprovideComponentRegistry(props.components);\nprovideRenderers(props.renderers);\n</script>\n","import { h } from 'vue';\nimport { useComponentRegistry } from '../registries/component-registry';\n\n/**\n * Helper utilities for building renderers\n */\nexport function useItemRenderer() {\n const componentRegistry = useComponentRegistry();\n\n return {\n /**\n * Render a custom component by ID\n *\n * @param componentId - Component ID from schema\n * @param documentId - Document ID\n * @param props - Additional props to pass to component\n * @returns Rendered component or null if not found\n */\n renderCustomComponent: (componentId: string, documentId: string, props?: any) => {\n const Component = componentRegistry.get(componentId);\n\n if (!Component) {\n console.error(`Component \"${componentId}\" not found in registry`);\n return null;\n }\n\n return h(Component, { documentId, ...(props || {}) });\n },\n };\n}\n","import { onBeforeUnmount, ref, watch } from 'vue';\nimport { useAnchorRegistry } from '../registries/anchor-registry';\n\n/**\n * Register a DOM element as an anchor for menus\n *\n * @param documentId - Document ID\n * @param itemId - Item ID (typically matches the toolbar/menu item ID)\n * @returns Ref to attach to the element (use with :ref=\"anchorRef\")\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * const anchorRef = useRegisterAnchor(props.documentId, 'zoom-button');\n * </script>\n *\n * <template>\n * <button :ref=\"anchorRef\">Zoom</button>\n * </template>\n * ```\n */\nexport function useRegisterAnchor(documentId: string, itemId: string) {\n const registry = useAnchorRegistry();\n const elementRef = ref<HTMLElement | null>(null);\n\n // Function to set ref\n const setRef = (el: any) => {\n // Handle Vue 3 ref binding\n const element = el?.$el || el;\n\n // Unregister previous element if exists\n if (elementRef.value && elementRef.value !== element) {\n registry.unregister(documentId, itemId);\n }\n\n elementRef.value = element;\n\n // Register new element\n if (element) {\n registry.register(documentId, itemId, element);\n }\n };\n\n // Cleanup on unmount\n onBeforeUnmount(() => {\n if (elementRef.value) {\n registry.unregister(documentId, itemId);\n }\n });\n\n return setRef;\n}\n","import { h, toValue, type VNode, type MaybeRefOrGetter } from 'vue';\nimport { useUICapability, useUIState } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * High-level composable for rendering UI from schema\n *\n * Provides simple functions to render toolbars, sidebars, and modals.\n * Always passes isOpen state to renderers so they can control animations.\n *\n * Automatically subscribes to UI state changes for the given document.\n * @param documentId Document ID (can be ref, computed, getter, or plain value)\n */\nexport function useSchemaRenderer(documentId: MaybeRefOrGetter<string>) {\n const renderers = useRenderers();\n const { provides } = useUICapability();\n const { state: uiState } = useUIState(documentId);\n\n return {\n /**\n * Render a toolbar by placement and slot\n *\n * Always renders with isOpen state when toolbar exists in slot.\n *\n * @param placement - 'top' | 'bottom' | 'left' | 'right'\n * @param slot - Slot name (e.g. 'main', 'secondary')\n *\n * @example\n * ```vue\n * <component :is=\"renderToolbar('top', 'main')\" />\n * <component :is=\"renderToolbar('top', 'secondary')\" />\n * ```\n */\n renderToolbar: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string): VNode | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema || !provides.value || !uiState.value) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.value.activeToolbars[slotKey];\n\n // If no toolbar in this slot, nothing to render\n if (!toolbarSlot) return null;\n\n const toolbarSchema = schema.toolbars[toolbarSlot.toolbarId];\n if (!toolbarSchema) {\n console.warn(`Toolbar \"${toolbarSlot.toolbarId}\" not found in schema`);\n return null;\n }\n\n // Check if toolbar is closable\n const isClosable = !toolbarSchema.permanent;\n\n const handleClose = isClosable\n ? () => {\n provides.value?.forDocument(toValue(documentId)).closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n const ToolbarRenderer = renderers.toolbar;\n\n // ALWAYS render, pass isOpen state\n return h(ToolbarRenderer, {\n key: toolbarSlot.toolbarId,\n schema: toolbarSchema,\n documentId: toValue(documentId),\n isOpen: toolbarSlot.isOpen,\n onClose: handleClose,\n });\n },\n\n /**\n * Render a sidebar by placement and slot\n *\n * ALWAYS renders (when sidebar exists in slot) with isOpen state.\n * Your renderer controls whether to display or animate.\n *\n * @param placement - 'left' | 'right' | 'top' | 'bottom'\n * @param slot - Slot name (e.g. 'main', 'secondary', 'inspector')\n *\n * @example\n * ```vue\n * <component :is=\"renderSidebar('left', 'main')\" />\n * <component :is=\"renderSidebar('right', 'main')\" />\n * ```\n */\n renderSidebar: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string): VNode | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema || !provides.value || !uiState.value) return null;\n\n const slotKey = `${placement}-${slot}`;\n const sidebarSlot = uiState.value.activeSidebars[slotKey];\n\n // If no sidebar in this slot, nothing to render\n if (!sidebarSlot) return null;\n\n const sidebarSchema = schema.sidebars?.[sidebarSlot.sidebarId];\n if (!sidebarSchema) {\n console.warn(`Sidebar \"${sidebarSlot.sidebarId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n provides.value?.forDocument(toValue(documentId)).closeSidebarSlot(placement, slot);\n };\n\n const SidebarRenderer = renderers.sidebar;\n\n // ALWAYS render, pass isOpen state\n return h(SidebarRenderer, {\n key: sidebarSlot.sidebarId,\n schema: sidebarSchema,\n documentId: toValue(documentId),\n isOpen: sidebarSlot.isOpen,\n onClose: handleClose,\n });\n },\n\n /**\n * Render the active modal (if any)\n *\n * Only one modal can be active at a time.\n * Modals are defined in schema.modals.\n *\n * Supports animation lifecycle:\n * - isOpen: true = visible\n * - isOpen: false = animate out (modal still rendered)\n * - onExited called after animation → modal removed\n *\n * @example\n * ```vue\n * <component :is=\"renderModal()\" />\n * ```\n */\n renderModal: (): VNode | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema || !provides.value || !uiState.value?.activeModal) return null;\n\n const { modalId, isOpen } = uiState.value.activeModal;\n\n const modalSchema = schema.modals?.[modalId];\n if (!modalSchema) {\n console.warn(`Modal \"${modalId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n provides.value?.forDocument(toValue(documentId)).closeModal();\n };\n\n const handleExited = () => {\n provides.value?.forDocument(toValue(documentId)).clearModal();\n };\n\n const ModalRenderer = renderers.modal;\n if (!ModalRenderer) {\n console.warn('No modal renderer registered');\n return null;\n }\n\n return h(ModalRenderer, {\n key: modalId,\n schema: modalSchema,\n documentId: toValue(documentId),\n isOpen,\n onClose: handleClose,\n onExited: handleExited,\n });\n },\n\n /**\n * Helper: Get all active toolbars for this document\n * Useful for batch rendering or debugging\n */\n getActiveToolbars: () => {\n if (!uiState.value) return [];\n return Object.entries(uiState.value.activeToolbars).map(([slotKey, toolbarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n toolbarId: toolbarSlot.toolbarId,\n isOpen: toolbarSlot.isOpen,\n };\n });\n },\n\n /**\n * Helper: Get all active sidebars for this document\n * Useful for batch rendering or debugging\n */\n getActiveSidebars: () => {\n if (!uiState.value) return [];\n return Object.entries(uiState.value.activeSidebars).map(([slotKey, sidebarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n sidebarId: sidebarSlot.sidebarId,\n isOpen: sidebarSlot.isOpen,\n };\n });\n },\n\n /**\n * Render all enabled overlays\n *\n * Overlays are floating components positioned over the document content.\n * Unlike modals, multiple overlays can be visible and they don't block interaction.\n *\n * @example\n * ```vue\n * <div class=\"relative\">\n * <slot />\n * <component :is=\"renderOverlays()\" />\n * </div>\n * ```\n */\n renderOverlays: (): VNode[] | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema?.overlays || !provides.value) return null;\n\n const OverlayRenderer = renderers.overlay;\n if (!OverlayRenderer) {\n return null;\n }\n\n const overlays = Object.values(schema.overlays);\n if (overlays.length === 0) return null;\n\n return overlays.map((overlaySchema) =>\n h(OverlayRenderer, {\n key: overlaySchema.id,\n schema: overlaySchema,\n documentId: toValue(documentId),\n }),\n );\n },\n };\n}\n","import { computed, h, toValue, type VNode, type MaybeRefOrGetter } from 'vue';\nimport type { SelectionMenuPropsBase, SelectionMenuRenderFn } from '@embedpdf/utils/vue';\nimport { useUICapability } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * Creates a render function for a selection menu from the schema\n *\n * @param menuId - The selection menu ID from schema\n * @param documentId - Document ID (can be ref, computed, getter, or plain value)\n * @returns A computed ref containing the render function or undefined\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * const annotationMenu = useSelectionMenu('annotation', () => props.documentId);\n * </script>\n *\n * <template>\n * <AnnotationLayer\n * :documentId=\"documentId\"\n * :selectionMenu=\"annotationMenu\"\n * />\n * </template>\n * ```\n */\nexport function useSelectionMenu<TContext extends { type: string } = { type: string }>(\n menuId: MaybeRefOrGetter<string>,\n documentId: MaybeRefOrGetter<string>,\n) {\n const { provides } = useUICapability();\n const renderers = useRenderers();\n\n const schema = computed(() => provides.value?.getSchema());\n const menuSchema = computed(() => schema.value?.selectionMenus?.[toValue(menuId)]);\n\n // Return a computed that produces the render function (or undefined)\n const renderFn = computed<SelectionMenuRenderFn<TContext> | undefined>(() => {\n // If no schema for this menu, return undefined\n if (!menuSchema.value) {\n return undefined;\n }\n\n // Capture current values for the closure\n const currentMenuSchema = menuSchema.value;\n const currentDocumentId = toValue(documentId);\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n // Return the render function\n return (props: SelectionMenuPropsBase<TContext>): VNode | null => {\n if (!props.selected) {\n return null;\n }\n\n return h(SelectionMenuRenderer, {\n schema: currentMenuSchema,\n documentId: currentDocumentId,\n props,\n });\n };\n });\n\n return renderFn;\n}\n"],"names":["useUIPlugin","usePlugin","UIPlugin","id","useUICapability","useCapability","useUIState","documentId","provides","state","ref","watch","toValue","providesValue","docId","_","onCleanup","value","scope","forDocument","getState","unsubToolbar","onToolbarChanged","unsubSidebar","onSidebarChanged","unsubModal","onModalChanged","unsubMenu","onMenuChanged","immediate","computed","_a","readonly","UI_CONTAINER_KEY","Symbol","AnchorRegistryKey","createAnchorRegistry","anchors","Map","register","itemId","element","key","set","unregister","delete","getAnchor","get","provideAnchorRegistry","registry","provide","useAnchorRegistry","inject","Error","ComponentRegistryKey","createComponentRegistry","initialComponents","components","Object","entries","component","has","getRegisteredIds","Array","from","keys","provideComponentRegistry","useComponentRegistry","RenderersKey","provideRenderers","renderers","useRenderers","props","__props","uiState","anchorRegistry","activeMenu","openMenus","schema","getSchema","menus","openMenuIds","length","menuId","menuState","triggeredByItemId","anchor","anchorEl","menuSchema","menuSchemaValue","console","warn","handleClose","closeMenu","MenuRenderer","menu","_openBlock","_createBlock","_resolveDynamicComponent","onClose","container","attrs","useAttrs","plugin","disabledCategories","hiddenItems","rootRef","containerContext","containerRef","getContainer","styleEl","styleTarget","injectStyles","root","getRootNode","ShadowRoot","document","head","getStyleTarget","existingStyle","querySelector","UI_SELECTORS","STYLES","textContent","getStylesheet","stylesheet","newStyleEl","createElement","setAttribute","UI_ATTRIBUTES","insertBefore","firstChild","appendChild","rootAttrs","result","ROOT","DISABLED_CATEGORIES","join","HIDDEN_ITEMS","stylesheetCleanup","categoryCleanup","onMounted","onStylesheetInvalidated","getDisabledCategories","getHiddenItems","onCategoryChanged","event","onUnmounted","parentNode","remove","_createElementBlock","_mergeProps","_unref","style","containerType","_renderSlot","_ctx","$slots","UIRoot","_createVNode","AutoMenuRenderer","menuContainer","componentRegistry","renderCustomComponent","componentId","Component","h","error","elementRef","onBeforeUnmount","el","$el","renderToolbar","placement","slot","slotKey","toolbarSlot","activeToolbars","toolbarSchema","toolbars","toolbarId","permanent","closeToolbarSlot","ToolbarRenderer","toolbar","isOpen","renderSidebar","sidebarSlot","activeSidebars","sidebarSchema","_b","sidebars","sidebarId","SidebarRenderer","sidebar","closeSidebarSlot","renderModal","activeModal","modalId","modalSchema","_c","modals","ModalRenderer","modal","closeModal","onExited","clearModal","getActiveToolbars","map","split","getActiveSidebars","renderOverlays","overlays","OverlayRenderer","overlay","values","overlaySchema","selectionMenus","currentMenuSchema","currentDocumentId","SelectionMenuRenderer","selectionMenu","selected","context"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/vue/hooks/use-ui.ts","../../src/vue/hooks/use-ui-container.ts","../../src/vue/registries/anchor-registry.ts","../../src/vue/registries/component-registry.ts","../../src/vue/registries/renderers-registry.ts","../../src/vue/auto-menu-renderer.vue","../../src/vue/root.vue","../../src/vue/provider.vue","../../src/vue/hooks/use-item-renderer.ts","../../src/vue/hooks/use-register-anchor.ts","../../src/vue/hooks/use-schema-renderer.ts","../../src/vue/hooks/use-selection-menu.ts"],"sourcesContent":["import { ref, watch, computed, readonly, toValue, type MaybeRefOrGetter } from 'vue';\nimport { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { UIPlugin, UIDocumentState, UISchema } from '@embedpdf/plugin-ui';\n\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\n\n/**\n * Hook for UI state for a specific document\n * @param documentId Document ID (can be ref, computed, getter, or plain value)\n */\nexport const useUIState = (documentId: MaybeRefOrGetter<string>) => {\n const { provides } = useUICapability();\n const state = ref<UIDocumentState | null>(null);\n\n watch(\n [provides, () => toValue(documentId)],\n ([providesValue, docId], _, onCleanup) => {\n if (!providesValue) {\n state.value = null;\n return;\n }\n\n const scope = providesValue.forDocument(docId);\n\n // Set initial state\n state.value = scope.getState();\n\n // Subscribe to all changes\n const unsubToolbar = scope.onToolbarChanged(() => {\n state.value = scope.getState();\n });\n const unsubSidebar = scope.onSidebarChanged(() => {\n state.value = scope.getState();\n });\n const unsubModal = scope.onModalChanged(() => {\n state.value = scope.getState();\n });\n const unsubMenu = scope.onMenuChanged(() => {\n state.value = scope.getState();\n });\n const unsubOverlay = scope.onOverlayChanged(() => {\n state.value = scope.getState();\n });\n\n onCleanup(() => {\n unsubToolbar();\n unsubSidebar();\n unsubModal();\n unsubMenu();\n unsubOverlay();\n });\n },\n { immediate: true },\n );\n\n // Return a computed ref for the scoped capability\n const scopedProvides = computed(() => {\n const docId = toValue(documentId);\n return provides.value?.forDocument(docId) ?? null;\n });\n\n return {\n provides: scopedProvides,\n state: readonly(state),\n };\n};\n\n/**\n * Hook to get UI schema\n */\nexport const useUISchema = () => {\n const { provides } = useUICapability();\n const schema = computed<UISchema | null>(() => provides.value?.getSchema() ?? null);\n\n return readonly(schema);\n};\n","import { inject, type Ref, type InjectionKey } from 'vue';\n\nexport interface UIContainerContextValue {\n /** Reference to the UIRoot container element */\n containerRef: Ref<HTMLDivElement | null>;\n /** Get the container element (may be null if not mounted) */\n getContainer: () => HTMLDivElement | null;\n}\n\nexport const UI_CONTAINER_KEY: InjectionKey<UIContainerContextValue> = Symbol('ui-container');\n\n/**\n * Hook to access the UI container element.\n *\n * This provides access to the UIRoot container for:\n * - Container query based responsiveness\n * - Portaling elements to the root\n * - Measuring container dimensions\n *\n * @example\n * ```vue\n * <script setup>\n * import { useUIContainer } from '@embedpdf/plugin-ui/vue';\n * import { onMounted, onUnmounted } from 'vue';\n *\n * const { containerRef, getContainer } = useUIContainer();\n *\n * onMounted(() => {\n * const container = getContainer();\n * if (!container) return;\n *\n * const observer = new ResizeObserver(() => {\n * console.log('Container width:', container.clientWidth);\n * });\n * observer.observe(container);\n *\n * onUnmounted(() => observer.disconnect());\n * });\n * </script>\n * ```\n */\nexport function useUIContainer(): UIContainerContextValue {\n const context = inject(UI_CONTAINER_KEY);\n if (!context) {\n throw new Error('useUIContainer must be used within a UIProvider');\n }\n return context;\n}\n","import { ref, inject, provide, type InjectionKey, type Ref } from 'vue';\n\n/**\n * Anchor Registry\n *\n * Tracks DOM elements for menu positioning.\n * Each anchor is scoped by documentId and itemId.\n */\nexport interface AnchorRegistry {\n register(documentId: string, itemId: string, element: HTMLElement): void;\n unregister(documentId: string, itemId: string): void;\n getAnchor(documentId: string, itemId: string): HTMLElement | null;\n}\n\nconst AnchorRegistryKey: InjectionKey<AnchorRegistry> = Symbol('AnchorRegistry');\n\nexport function createAnchorRegistry(): AnchorRegistry {\n const anchors: Ref<Map<string, HTMLElement>> = ref(new Map());\n\n return {\n register(documentId: string, itemId: string, element: HTMLElement) {\n const key = `${documentId}:${itemId}`;\n anchors.value.set(key, element);\n },\n\n unregister(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n anchors.value.delete(key);\n },\n\n getAnchor(documentId: string, itemId: string) {\n const key = `${documentId}:${itemId}`;\n return anchors.value.get(key) || null;\n },\n };\n}\n\nexport function provideAnchorRegistry() {\n const registry = createAnchorRegistry();\n provide(AnchorRegistryKey, registry);\n return registry;\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const registry = inject(AnchorRegistryKey);\n if (!registry) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { ref, inject, provide, type Component, type InjectionKey, type Ref } from 'vue';\nimport type { BaseComponentProps } from '../types';\n\n/**\n * Component Registry\n *\n * Stores custom components that can be referenced in the UI schema.\n */\nexport interface ComponentRegistry {\n register(id: string, component: Component<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): Component<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst ComponentRegistryKey: InjectionKey<ComponentRegistry> = Symbol('ComponentRegistry');\n\nexport function createComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n): ComponentRegistry {\n const components: Ref<Map<string, Component<BaseComponentProps>>> = ref(\n new Map(Object.entries(initialComponents)),\n );\n\n return {\n register(id: string, component: Component<BaseComponentProps>) {\n components.value.set(id, component);\n },\n\n unregister(id: string) {\n components.value.delete(id);\n },\n\n get(id: string) {\n return components.value.get(id);\n },\n\n has(id: string) {\n return components.value.has(id);\n },\n\n getRegisteredIds() {\n return Array.from(components.value.keys());\n },\n };\n}\n\nexport function provideComponentRegistry(\n initialComponents: Record<string, Component<BaseComponentProps>> = {},\n) {\n const registry = createComponentRegistry(initialComponents);\n provide(ComponentRegistryKey, registry);\n return registry;\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const registry = inject(ComponentRegistryKey);\n if (!registry) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return registry;\n}\n","import { inject, provide, type InjectionKey } from 'vue';\nimport type { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RenderersKey: InjectionKey<UIRenderers> = Symbol('Renderers');\n\nexport function provideRenderers(renderers: UIRenderers) {\n provide(RenderersKey, renderers);\n}\n\nexport function useRenderers(): UIRenderers {\n const renderers = inject(RenderersKey);\n if (!renderers) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return renderers;\n}\n","<template>\n <component\n v-if=\"activeMenu && menuSchema && MenuRenderer\"\n :is=\"MenuRenderer\"\n :schema=\"menuSchema\"\n :documentId=\"documentId\"\n :anchorEl=\"activeMenu.anchorEl\"\n :onClose=\"handleClose\"\n :container=\"container\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport { useUIState, useUICapability } from './hooks/use-ui';\nimport { useAnchorRegistry } from './registries/anchor-registry';\nimport { useRenderers } from './registries/renderers-registry';\n\n/**\n * Automatically renders menus when opened\n *\n * This component:\n * 1. Listens to UI plugin state for open menus\n * 2. Looks up anchor elements from the anchor registry\n * 3. Renders menus using the user-provided menu renderer\n */\n\ninterface Props {\n documentId: string; // Which document's menus to render\n container?: HTMLElement | null;\n}\n\nconst props = defineProps<Props>();\n\nconst { state: uiState } = useUIState(props.documentId);\nconst { provides } = useUICapability();\nconst anchorRegistry = useAnchorRegistry();\nconst renderers = useRenderers();\n\nconst activeMenu = ref<{\n menuId: string;\n anchorEl: HTMLElement | null;\n} | null>(null);\n\nconst openMenus = computed(() => uiState.value?.openMenus || {});\nconst schema = computed(() => provides.value?.getSchema());\n\n// Update active menu when state changes\nwatch(\n openMenus,\n (menus) => {\n const openMenuIds = Object.keys(menus);\n\n if (openMenuIds.length > 0) {\n // Show the first open menu (in practice, should only be one)\n const menuId = openMenuIds[0];\n if (!menuId) {\n activeMenu.value = null;\n return;\n }\n\n const menuState = menus[menuId];\n if (menuState && menuState.triggeredByItemId) {\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(props.documentId, menuState.triggeredByItemId);\n activeMenu.value = { menuId, anchorEl: anchor };\n } else {\n activeMenu.value = null;\n }\n } else {\n activeMenu.value = null;\n }\n },\n { immediate: true },\n);\n\nconst menuSchema = computed(() => {\n if (!activeMenu.value || !schema.value) return null;\n\n const menuSchemaValue = schema.value.menus[activeMenu.value.menuId];\n if (!menuSchemaValue) {\n console.warn(`Menu \"${activeMenu.value.menuId}\" not found in schema`);\n return null;\n }\n\n return menuSchemaValue;\n});\n\nconst handleClose = () => {\n if (activeMenu.value) {\n provides.value?.forDocument(props.documentId).closeMenu(activeMenu.value.menuId);\n }\n};\n\n// Use the user-provided menu renderer\nconst MenuRenderer = computed(() => renderers.menu);\n</script>\n","<template>\n <div\n ref=\"rootRef\"\n v-bind=\"{ ...attrs, ...(rootAttrs as any) }\"\n :style=\"{ containerType: 'inline-size' }\"\n >\n <slot />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted, watch, useAttrs, provide } from 'vue';\nimport { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\nimport { useUIPlugin, useUICapability } from './hooks/use-ui';\nimport { UI_CONTAINER_KEY, type UIContainerContextValue } from './hooks/use-ui-container';\n\n// Disable automatic attribute inheritance since we handle it manually\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst attrs = useAttrs();\n\nconst { plugin } = useUIPlugin();\nconst { provides } = useUICapability();\n\nconst disabledCategories = ref<string[]>([]);\nconst hiddenItems = ref<string[]>([]);\nconst rootRef = ref<HTMLDivElement | null>(null);\n\n// Provide container context for child components\nconst containerContext: UIContainerContextValue = {\n containerRef: rootRef,\n getContainer: () => rootRef.value,\n};\nprovide(UI_CONTAINER_KEY, containerContext);\n\nlet styleEl: HTMLStyleElement | null = null;\nlet styleTarget: HTMLElement | ShadowRoot | null = null;\n\n/**\n * Find the style injection target for an element.\n * Returns the shadow root if inside one, otherwise document.head.\n */\nfunction getStyleTarget(element: HTMLElement): HTMLElement | ShadowRoot {\n const root = element.getRootNode();\n if (root instanceof ShadowRoot) {\n return root;\n }\n return document.head;\n}\n\n/**\n * Inject or update stylesheet\n */\nfunction injectStyles() {\n if (!rootRef.value || !plugin.value) {\n return;\n }\n\n styleTarget = getStyleTarget(rootRef.value);\n\n // Check if styles already exist in this target\n const existingStyle = styleTarget.querySelector(UI_SELECTORS.STYLES) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleEl = existingStyle;\n // Update content in case locale changed\n existingStyle.textContent = plugin.value.getStylesheet();\n return;\n }\n\n // Create and inject stylesheet\n const stylesheet = plugin.value.getStylesheet();\n const newStyleEl = document.createElement('style');\n newStyleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n newStyleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n styleTarget.insertBefore(newStyleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(newStyleEl);\n }\n\n styleEl = newStyleEl;\n}\n\n/**\n * Cleanup styles\n */\nfunction cleanupStyles() {\n if (styleEl?.parentNode) {\n styleEl.remove();\n }\n styleEl = null;\n styleTarget = null;\n}\n\n// Build root element attributes\nconst rootAttrs = computed(() => {\n const result: Record<string, string> = {\n [UI_ATTRIBUTES.ROOT]: '',\n };\n\n if (disabledCategories.value.length > 0) {\n result[UI_ATTRIBUTES.DISABLED_CATEGORIES] = disabledCategories.value.join(' ');\n }\n\n if (hiddenItems.value.length > 0) {\n result[UI_ATTRIBUTES.HIDDEN_ITEMS] = hiddenItems.value.join(' ');\n }\n\n return result;\n});\n\n// Stylesheet invalidation cleanup\nlet stylesheetCleanup: (() => void) | null = null;\n\n// Category change cleanup\nlet categoryCleanup: (() => void) | null = null;\n\nonMounted(() => {\n // Inject styles on mount\n injectStyles();\n\n // Subscribe to stylesheet invalidation\n if (plugin.value) {\n stylesheetCleanup = plugin.value.onStylesheetInvalidated(() => {\n if (styleEl && plugin.value) {\n styleEl.textContent = plugin.value.getStylesheet();\n }\n });\n }\n\n // Subscribe to category changes\n if (provides.value) {\n disabledCategories.value = provides.value.getDisabledCategories();\n hiddenItems.value = provides.value.getHiddenItems();\n\n categoryCleanup = provides.value.onCategoryChanged((event) => {\n disabledCategories.value = event.disabledCategories;\n hiddenItems.value = event.hiddenItems;\n });\n }\n});\n\nonUnmounted(() => {\n cleanupStyles();\n stylesheetCleanup?.();\n categoryCleanup?.();\n});\n\n// Re-inject styles if plugin changes\nwatch(plugin, () => {\n if (rootRef.value && plugin.value) {\n injectStyles();\n }\n});\n</script>\n","<template>\n <UIRoot v-bind=\"attrs\">\n <slot />\n <!-- Automatically render menus for this document -->\n <AutoMenuRenderer :documentId=\"documentId\" :container=\"menuContainer\" />\n </UIRoot>\n</template>\n\n<script setup lang=\"ts\">\nimport type { Component } from 'vue';\nimport { useAttrs } from 'vue';\nimport { provideAnchorRegistry } from './registries/anchor-registry';\nimport { provideComponentRegistry } from './registries/component-registry';\nimport { provideRenderers } from './registries/renderers-registry';\nimport type { BaseComponentProps, UIRenderers } from './types';\nimport AutoMenuRenderer from './auto-menu-renderer.vue';\nimport UIRoot from './root.vue';\n\n// Disable automatic attribute inheritance since we pass them to UIRoot\ndefineOptions({\n inheritAttrs: false,\n});\n\nconst attrs = useAttrs();\n\n/**\n * UIProvider Props\n */\ninterface Props {\n /**\n * Document ID for this UI context\n * Required for menu rendering\n */\n documentId: string;\n\n /**\n * Custom component registry\n * Maps component IDs to components\n */\n components?: Record<string, Component<BaseComponentProps>>;\n\n /**\n * REQUIRED: User-provided renderers\n * These define how toolbars, panels, and menus are displayed\n */\n renderers: UIRenderers;\n\n /**\n * Optional: Container for menu portal\n * Defaults to document.body\n */\n menuContainer?: HTMLElement | null;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n components: () => ({}),\n menuContainer: null,\n});\n\n/**\n * UIProvider - Single provider for all UI plugin functionality\n *\n * Manages:\n * - Anchor registry for menu positioning\n * - Component registry for custom components\n * - Renderers for toolbars, panels, and menus\n * - Automatic menu rendering\n *\n * @example\n * ```vue\n * <EmbedPDF :engine=\"engine\" :plugins=\"plugins\">\n * <template v-slot=\"{ pluginsReady, activeDocumentId }\">\n * <UIProvider\n * v-if=\"pluginsReady && activeDocumentId\"\n * :documentId=\"activeDocumentId\"\n * :components=\"{\n * 'thumbnail-panel': ThumbnailPanel,\n * 'bookmark-panel': BookmarkPanel,\n * }\"\n * :renderers=\"{\n * toolbar: ToolbarRenderer,\n * panel: PanelRenderer,\n * menu: MenuRenderer,\n * }\"\n * class=\"relative flex h-full w-full\"\n * >\n * <ViewerLayout />\n * </UIProvider>\n * </template>\n * </EmbedPDF>\n * ```\n */\n\n// Provide all registries\nprovideAnchorRegistry();\nprovideComponentRegistry(props.components);\nprovideRenderers(props.renderers);\n</script>\n","import { h } from 'vue';\nimport { useComponentRegistry } from '../registries/component-registry';\n\n/**\n * Helper utilities for building renderers\n */\nexport function useItemRenderer() {\n const componentRegistry = useComponentRegistry();\n\n return {\n /**\n * Render a custom component by ID\n *\n * @param componentId - Component ID from schema\n * @param documentId - Document ID\n * @param props - Additional props to pass to component\n * @returns Rendered component or null if not found\n */\n renderCustomComponent: (componentId: string, documentId: string, props?: any) => {\n const Component = componentRegistry.get(componentId);\n\n if (!Component) {\n console.error(`Component \"${componentId}\" not found in registry`);\n return null;\n }\n\n return h(Component, { documentId, ...(props || {}) });\n },\n };\n}\n","import { onBeforeUnmount, ref, watch } from 'vue';\nimport { useAnchorRegistry } from '../registries/anchor-registry';\n\n/**\n * Register a DOM element as an anchor for menus\n *\n * @param documentId - Document ID\n * @param itemId - Item ID (typically matches the toolbar/menu item ID)\n * @returns Ref to attach to the element (use with :ref=\"anchorRef\")\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * const anchorRef = useRegisterAnchor(props.documentId, 'zoom-button');\n * </script>\n *\n * <template>\n * <button :ref=\"anchorRef\">Zoom</button>\n * </template>\n * ```\n */\nexport function useRegisterAnchor(documentId: string, itemId: string) {\n const registry = useAnchorRegistry();\n const elementRef = ref<HTMLElement | null>(null);\n\n // Function to set ref\n const setRef = (el: any) => {\n // Handle Vue 3 ref binding\n const element = el?.$el || el;\n\n // Unregister previous element if exists\n if (elementRef.value && elementRef.value !== element) {\n registry.unregister(documentId, itemId);\n }\n\n elementRef.value = element;\n\n // Register new element\n if (element) {\n registry.register(documentId, itemId, element);\n }\n };\n\n // Cleanup on unmount\n onBeforeUnmount(() => {\n if (elementRef.value) {\n registry.unregister(documentId, itemId);\n }\n });\n\n return setRef;\n}\n","import { h, toValue, type VNode, type MaybeRefOrGetter } from 'vue';\nimport { useUICapability, useUIState } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * High-level composable for rendering UI from schema\n *\n * Provides simple functions to render toolbars, sidebars, and modals.\n * Always passes isOpen state to renderers so they can control animations.\n *\n * Automatically subscribes to UI state changes for the given document.\n * @param documentId Document ID (can be ref, computed, getter, or plain value)\n */\nexport function useSchemaRenderer(documentId: MaybeRefOrGetter<string>) {\n const renderers = useRenderers();\n const { provides } = useUICapability();\n const { state: uiState } = useUIState(documentId);\n\n return {\n /**\n * Render a toolbar by placement and slot\n *\n * Always renders with isOpen state when toolbar exists in slot.\n *\n * @param placement - 'top' | 'bottom' | 'left' | 'right'\n * @param slot - Slot name (e.g. 'main', 'secondary')\n *\n * @example\n * ```vue\n * <component :is=\"renderToolbar('top', 'main')\" />\n * <component :is=\"renderToolbar('top', 'secondary')\" />\n * ```\n */\n renderToolbar: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string): VNode | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema || !provides.value || !uiState.value) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.value.activeToolbars[slotKey];\n\n // If no toolbar in this slot, nothing to render\n if (!toolbarSlot) return null;\n\n const toolbarSchema = schema.toolbars[toolbarSlot.toolbarId];\n if (!toolbarSchema) {\n console.warn(`Toolbar \"${toolbarSlot.toolbarId}\" not found in schema`);\n return null;\n }\n\n // Check if toolbar is closable\n const isClosable = !toolbarSchema.permanent;\n\n const handleClose = isClosable\n ? () => {\n provides.value?.forDocument(toValue(documentId)).closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n const ToolbarRenderer = renderers.toolbar;\n\n // ALWAYS render, pass isOpen state\n return h(ToolbarRenderer, {\n key: toolbarSlot.toolbarId,\n schema: toolbarSchema,\n documentId: toValue(documentId),\n isOpen: toolbarSlot.isOpen,\n onClose: handleClose,\n });\n },\n\n /**\n * Render a sidebar by placement and slot\n *\n * ALWAYS renders (when sidebar exists in slot) with isOpen state.\n * Your renderer controls whether to display or animate.\n *\n * @param placement - 'left' | 'right' | 'top' | 'bottom'\n * @param slot - Slot name (e.g. 'main', 'secondary', 'inspector')\n *\n * @example\n * ```vue\n * <component :is=\"renderSidebar('left', 'main')\" />\n * <component :is=\"renderSidebar('right', 'main')\" />\n * ```\n */\n renderSidebar: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string): VNode | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema || !provides.value || !uiState.value) return null;\n\n const slotKey = `${placement}-${slot}`;\n const sidebarSlot = uiState.value.activeSidebars[slotKey];\n\n // If no sidebar in this slot, nothing to render\n if (!sidebarSlot) return null;\n\n const sidebarSchema = schema.sidebars?.[sidebarSlot.sidebarId];\n if (!sidebarSchema) {\n console.warn(`Sidebar \"${sidebarSlot.sidebarId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n provides.value?.forDocument(toValue(documentId)).closeSidebarSlot(placement, slot);\n };\n\n const SidebarRenderer = renderers.sidebar;\n\n // ALWAYS render, pass isOpen state\n return h(SidebarRenderer, {\n key: sidebarSlot.sidebarId,\n schema: sidebarSchema,\n documentId: toValue(documentId),\n isOpen: sidebarSlot.isOpen,\n onClose: handleClose,\n });\n },\n\n /**\n * Render the active modal (if any)\n *\n * Only one modal can be active at a time.\n * Modals are defined in schema.modals.\n *\n * Supports animation lifecycle:\n * - isOpen: true = visible\n * - isOpen: false = animate out (modal still rendered)\n * - onExited called after animation → modal removed\n *\n * @example\n * ```vue\n * <component :is=\"renderModal()\" />\n * ```\n */\n renderModal: (): VNode | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema || !provides.value || !uiState.value?.activeModal) return null;\n\n const { modalId, isOpen } = uiState.value.activeModal;\n\n const modalSchema = schema.modals?.[modalId];\n if (!modalSchema) {\n console.warn(`Modal \"${modalId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n provides.value?.forDocument(toValue(documentId)).closeModal();\n };\n\n const handleExited = () => {\n provides.value?.forDocument(toValue(documentId)).clearModal();\n };\n\n const ModalRenderer = renderers.modal;\n if (!ModalRenderer) {\n console.warn('No modal renderer registered');\n return null;\n }\n\n return h(ModalRenderer, {\n key: modalId,\n schema: modalSchema,\n documentId: toValue(documentId),\n isOpen,\n onClose: handleClose,\n onExited: handleExited,\n });\n },\n\n /**\n * Helper: Get all active toolbars for this document\n * Useful for batch rendering or debugging\n */\n getActiveToolbars: () => {\n if (!uiState.value) return [];\n return Object.entries(uiState.value.activeToolbars).map(([slotKey, toolbarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n toolbarId: toolbarSlot.toolbarId,\n isOpen: toolbarSlot.isOpen,\n };\n });\n },\n\n /**\n * Helper: Get all active sidebars for this document\n * Useful for batch rendering or debugging\n */\n getActiveSidebars: () => {\n if (!uiState.value) return [];\n return Object.entries(uiState.value.activeSidebars).map(([slotKey, sidebarSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n sidebarId: sidebarSlot.sidebarId,\n isOpen: sidebarSlot.isOpen,\n };\n });\n },\n\n /**\n * Render all enabled overlays\n *\n * Overlays are floating components positioned over the document content.\n * Unlike modals, multiple overlays can be visible and they don't block interaction.\n * Overlay visibility is controlled by the enabledOverlays state.\n *\n * @example\n * ```vue\n * <div class=\"relative\">\n * <slot />\n * <component :is=\"renderOverlays()\" />\n * </div>\n * ```\n */\n renderOverlays: (): VNode[] | null => {\n const schema = provides.value?.getSchema();\n\n if (!schema?.overlays || !provides.value || !uiState.value) return null;\n\n const OverlayRenderer = renderers.overlay;\n if (!OverlayRenderer) {\n return null;\n }\n\n const overlays = Object.values(schema.overlays);\n if (overlays.length === 0) return null;\n\n // Filter overlays by enabled state (default to true if not explicitly set)\n const enabledOverlays = overlays.filter(\n (overlay) => uiState.value!.enabledOverlays[overlay.id] !== false,\n );\n\n return enabledOverlays.map((overlaySchema) =>\n h(OverlayRenderer, {\n key: overlaySchema.id,\n schema: overlaySchema,\n documentId: toValue(documentId),\n }),\n );\n },\n };\n}\n","import { computed, h, toValue, type VNode, type MaybeRefOrGetter } from 'vue';\nimport type { SelectionMenuPropsBase, SelectionMenuRenderFn } from '@embedpdf/utils/vue';\nimport { useUICapability } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * Creates a render function for a selection menu from the schema\n *\n * @param menuId - The selection menu ID from schema\n * @param documentId - Document ID (can be ref, computed, getter, or plain value)\n * @returns A computed ref containing the render function or undefined\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * const annotationMenu = useSelectionMenu('annotation', () => props.documentId);\n * </script>\n *\n * <template>\n * <AnnotationLayer\n * :documentId=\"documentId\"\n * :selectionMenu=\"annotationMenu\"\n * />\n * </template>\n * ```\n */\nexport function useSelectionMenu<TContext extends { type: string } = { type: string }>(\n menuId: MaybeRefOrGetter<string>,\n documentId: MaybeRefOrGetter<string>,\n) {\n const { provides } = useUICapability();\n const renderers = useRenderers();\n\n const schema = computed(() => provides.value?.getSchema());\n const menuSchema = computed(() => schema.value?.selectionMenus?.[toValue(menuId)]);\n\n // Return a computed that produces the render function (or undefined)\n const renderFn = computed<SelectionMenuRenderFn<TContext> | undefined>(() => {\n // If no schema for this menu, return undefined\n if (!menuSchema.value) {\n return undefined;\n }\n\n // Capture current values for the closure\n const currentMenuSchema = menuSchema.value;\n const currentDocumentId = toValue(documentId);\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n // Return the render function\n return (props: SelectionMenuPropsBase<TContext>): VNode | null => {\n if (!props.selected) {\n return null;\n }\n\n return h(SelectionMenuRenderer, {\n schema: currentMenuSchema,\n documentId: currentDocumentId,\n props,\n });\n };\n });\n\n return renderFn;\n}\n"],"names":["useUIPlugin","usePlugin","UIPlugin","id","useUICapability","useCapability","useUIState","documentId","provides","state","ref","watch","toValue","providesValue","docId","_","onCleanup","value","scope","forDocument","getState","unsubToolbar","onToolbarChanged","unsubSidebar","onSidebarChanged","unsubModal","onModalChanged","unsubMenu","onMenuChanged","unsubOverlay","onOverlayChanged","immediate","computed","_a","readonly","UI_CONTAINER_KEY","Symbol","AnchorRegistryKey","createAnchorRegistry","anchors","Map","register","itemId","element","key","set","unregister","delete","getAnchor","get","provideAnchorRegistry","registry","provide","useAnchorRegistry","inject","Error","ComponentRegistryKey","createComponentRegistry","initialComponents","components","Object","entries","component","has","getRegisteredIds","Array","from","keys","provideComponentRegistry","useComponentRegistry","RenderersKey","provideRenderers","renderers","useRenderers","props","__props","uiState","anchorRegistry","activeMenu","openMenus","schema","getSchema","menus","openMenuIds","length","menuId","menuState","triggeredByItemId","anchor","anchorEl","menuSchema","menuSchemaValue","console","warn","handleClose","closeMenu","MenuRenderer","menu","_openBlock","_createBlock","_resolveDynamicComponent","onClose","container","attrs","useAttrs","plugin","disabledCategories","hiddenItems","rootRef","containerContext","containerRef","getContainer","styleEl","styleTarget","injectStyles","root","getRootNode","ShadowRoot","document","head","getStyleTarget","existingStyle","querySelector","UI_SELECTORS","STYLES","textContent","getStylesheet","stylesheet","newStyleEl","createElement","setAttribute","UI_ATTRIBUTES","insertBefore","firstChild","appendChild","rootAttrs","result","ROOT","DISABLED_CATEGORIES","join","HIDDEN_ITEMS","stylesheetCleanup","categoryCleanup","onMounted","onStylesheetInvalidated","getDisabledCategories","getHiddenItems","onCategoryChanged","event","onUnmounted","parentNode","remove","_createElementBlock","_mergeProps","_unref","style","containerType","_renderSlot","_ctx","$slots","UIRoot","_createVNode","AutoMenuRenderer","menuContainer","componentRegistry","renderCustomComponent","componentId","Component","h","error","elementRef","onBeforeUnmount","el","$el","renderToolbar","placement","slot","slotKey","toolbarSlot","activeToolbars","toolbarSchema","toolbars","toolbarId","permanent","closeToolbarSlot","ToolbarRenderer","toolbar","isOpen","renderSidebar","sidebarSlot","activeSidebars","sidebarSchema","_b","sidebars","sidebarId","SidebarRenderer","sidebar","closeSidebarSlot","renderModal","activeModal","modalId","modalSchema","_c","modals","ModalRenderer","modal","closeModal","onExited","clearModal","getActiveToolbars","map","split","getActiveSidebars","renderOverlays","overlays","OverlayRenderer","overlay","values","filter","enabledOverlays","overlaySchema","selectionMenus","currentMenuSchema","currentDocumentId","SelectionMenuRenderer","selectionMenu","selected","context"],"mappings":"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"}
|
package/dist/vue/index.js
CHANGED
|
@@ -28,11 +28,15 @@ const useUIState = (documentId) => {
|
|
|
28
28
|
const unsubMenu = scope.onMenuChanged(() => {
|
|
29
29
|
state.value = scope.getState();
|
|
30
30
|
});
|
|
31
|
+
const unsubOverlay = scope.onOverlayChanged(() => {
|
|
32
|
+
state.value = scope.getState();
|
|
33
|
+
});
|
|
31
34
|
onCleanup(() => {
|
|
32
35
|
unsubToolbar();
|
|
33
36
|
unsubSidebar();
|
|
34
37
|
unsubModal();
|
|
35
38
|
unsubMenu();
|
|
39
|
+
unsubOverlay();
|
|
36
40
|
});
|
|
37
41
|
},
|
|
38
42
|
{ immediate: true }
|
|
@@ -350,6 +354,7 @@ function useSchemaRenderer(documentId) {
|
|
|
350
354
|
*
|
|
351
355
|
* Overlays are floating components positioned over the document content.
|
|
352
356
|
* Unlike modals, multiple overlays can be visible and they don't block interaction.
|
|
357
|
+
* Overlay visibility is controlled by the enabledOverlays state.
|
|
353
358
|
*
|
|
354
359
|
* @example
|
|
355
360
|
* ```vue
|
|
@@ -362,14 +367,17 @@ function useSchemaRenderer(documentId) {
|
|
|
362
367
|
renderOverlays: () => {
|
|
363
368
|
var _a;
|
|
364
369
|
const schema = (_a = provides.value) == null ? void 0 : _a.getSchema();
|
|
365
|
-
if (!(schema == null ? void 0 : schema.overlays) || !provides.value) return null;
|
|
370
|
+
if (!(schema == null ? void 0 : schema.overlays) || !provides.value || !uiState.value) return null;
|
|
366
371
|
const OverlayRenderer = renderers.overlay;
|
|
367
372
|
if (!OverlayRenderer) {
|
|
368
373
|
return null;
|
|
369
374
|
}
|
|
370
375
|
const overlays = Object.values(schema.overlays);
|
|
371
376
|
if (overlays.length === 0) return null;
|
|
372
|
-
|
|
377
|
+
const enabledOverlays = overlays.filter(
|
|
378
|
+
(overlay) => uiState.value.enabledOverlays[overlay.id] !== false
|
|
379
|
+
);
|
|
380
|
+
return enabledOverlays.map(
|
|
373
381
|
(overlaySchema) => h(OverlayRenderer, {
|
|
374
382
|
key: overlaySchema.id,
|
|
375
383
|
schema: overlaySchema,
|