@embedpdf/plugin-ui 2.0.0-next.1 → 2.0.0-next.2
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 +235 -146
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +31 -15
- package/dist/lib/schema.d.ts +51 -10
- package/dist/lib/selectors.d.ts +5 -5
- package/dist/lib/types.d.ts +39 -23
- package/dist/lib/ui-plugin.d.ts +11 -8
- package/dist/lib/utils/consts.d.ts +3 -0
- package/dist/lib/utils/schema-merger.d.ts +1 -1
- package/dist/lib/utils/stylesheet-generator.d.ts +17 -0
- package/dist/preact/adapter.d.ts +1 -1
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +143 -38
- package/dist/preact/index.js.map +1 -1
- package/dist/react/adapter.d.ts +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +143 -38
- package/dist/react/index.js.map +1 -1
- package/dist/shared/hooks/index.d.ts +1 -0
- package/dist/shared/hooks/use-schema-renderer.d.ts +41 -9
- package/dist/shared/hooks/use-ui-container.d.ts +39 -0
- package/dist/shared/root.d.ts +1 -1
- package/dist/shared/types.d.ts +31 -6
- package/dist/shared-preact/hooks/index.d.ts +1 -0
- package/dist/shared-preact/hooks/use-schema-renderer.d.ts +41 -9
- package/dist/shared-preact/hooks/use-ui-container.d.ts +39 -0
- package/dist/shared-preact/root.d.ts +1 -1
- package/dist/shared-preact/types.d.ts +31 -6
- package/dist/shared-react/hooks/index.d.ts +1 -0
- package/dist/shared-react/hooks/use-schema-renderer.d.ts +41 -9
- package/dist/shared-react/hooks/use-ui-container.d.ts +39 -0
- package/dist/shared-react/root.d.ts +1 -1
- package/dist/shared-react/types.d.ts +31 -6
- package/dist/svelte/hooks/index.d.ts +1 -0
- package/dist/svelte/hooks/use-schema-renderer.svelte.d.ts +55 -12
- package/dist/svelte/hooks/use-ui-container.svelte.d.ts +41 -0
- package/dist/svelte/hooks/use-ui.svelte.d.ts +2 -2
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +112 -20
- package/dist/svelte/index.js.map +1 -1
- package/dist/svelte/types.d.ts +31 -6
- package/dist/vue/hooks/index.d.ts +1 -0
- package/dist/vue/hooks/use-schema-renderer.d.ts +41 -9
- package/dist/vue/hooks/use-ui-container.d.ts +39 -0
- package/dist/vue/hooks/use-ui.d.ts +148 -20
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +126 -25
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/types.d.ts +31 -6
- package/package.json +12 -12
package/dist/preact/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-ui.ts","../../src/shared/registries/anchor-registry.tsx","../../src/shared/hooks/use-register-anchor.ts","../../src/shared/registries/component-registry.tsx","../../src/shared/hooks/use-item-renderer.tsx","../../src/shared/registries/renderers-registry.tsx","../../src/shared/hooks/use-schema-renderer.tsx","../../src/shared/hooks/use-selection-menu.tsx","../../src/shared/auto-menu-renderer.tsx","../../src/shared/root.tsx","../../src/shared/provider.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { UIPlugin } from '@embedpdf/plugin-ui';\nimport { useState, useEffect } from '@framework';\nimport { UIDocumentState, UISchema } from '@embedpdf/plugin-ui';\n\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\n\n/**\n * Get UI state for a document\n */\nexport const useUIState = (documentId: string) => {\n const { provides } = useUICapability();\n const [state, setState] = useState<UIDocumentState | null>(null);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n setState(scope.getState());\n\n // Subscribe to changes\n const unsubToolbar = scope.onToolbarChanged(() => setState(scope.getState()));\n const unsubPanel = scope.onPanelChanged(() => setState(scope.getState()));\n const unsubModal = scope.onModalChanged(() => setState(scope.getState()));\n const unsubMenu = scope.onMenuChanged(() => setState(scope.getState()));\n\n return () => {\n unsubToolbar();\n unsubPanel();\n unsubModal();\n unsubMenu();\n };\n }, [provides, documentId]);\n\n return state;\n};\n\n/**\n * Get UI schema\n */\nexport const useUISchema = (): UISchema | null => {\n const { provides } = useUICapability();\n return provides?.getSchema() ?? null;\n};\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ReactNode } from '@framework';\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 AnchorRegistryContext = createContext<AnchorRegistry | null>(null);\n\nexport function AnchorRegistryProvider({ children }: { children: ReactNode }) {\n const anchorsRef = useRef<Map<string, HTMLElement>>(new Map());\n\n const registry: AnchorRegistry = {\n register: useCallback((documentId: string, itemId: string, element: HTMLElement) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.set(key, element);\n }, []),\n\n unregister: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.delete(key);\n }, []),\n\n getAnchor: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n return anchorsRef.current.get(key) || null;\n }, []),\n };\n\n return (\n <AnchorRegistryContext.Provider value={registry}>{children}</AnchorRegistryContext.Provider>\n );\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const context = useContext(AnchorRegistryContext);\n if (!context) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { useCallback, useRef } from '@framework';\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 callback to attach to the element\n *\n * @example\n * ```tsx\n * function ZoomButton({ documentId }: Props) {\n * const anchorRef = useRegisterAnchor(documentId, 'zoom-button');\n *\n * return <button ref={anchorRef}>Zoom</button>;\n * }\n * ```\n */\nexport function useRegisterAnchor(\n documentId: string,\n itemId: string,\n): (element: HTMLElement | null) => void {\n const registry = useAnchorRegistry();\n const elementRef = useRef<HTMLElement | null>(null);\n const documentIdRef = useRef(documentId);\n const itemIdRef = useRef(itemId);\n\n // Keep refs in sync\n documentIdRef.current = documentId;\n itemIdRef.current = itemId;\n\n // Return stable callback that uses refs\n return useCallback(\n (element: HTMLElement | null) => {\n // Store previous element\n const previousElement = elementRef.current;\n\n // Update ref\n elementRef.current = element;\n\n // Handle registration/unregistration\n if (element) {\n // Register new element\n if (element !== previousElement) {\n registry.register(documentIdRef.current, itemIdRef.current, element);\n }\n } else if (previousElement) {\n // Element is now null, but we had one before - unregister\n registry.unregister(documentIdRef.current, itemIdRef.current);\n }\n },\n [registry], // Only depend on registry!\n );\n}\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ComponentType, ReactNode } from '@framework';\nimport { 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: ComponentType<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): ComponentType<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst ComponentRegistryContext = createContext<ComponentRegistry | null>(null);\n\nexport interface ComponentRegistryProviderProps {\n children: ReactNode;\n initialComponents?: Record<string, ComponentType<BaseComponentProps>>;\n}\n\nexport function ComponentRegistryProvider({\n children,\n initialComponents = {},\n}: ComponentRegistryProviderProps) {\n const componentsRef = useRef<Map<string, ComponentType<BaseComponentProps>>>(\n new Map(Object.entries(initialComponents)),\n );\n\n const registry: ComponentRegistry = {\n register: useCallback((id: string, component: ComponentType<BaseComponentProps>) => {\n componentsRef.current.set(id, component);\n }, []),\n\n unregister: useCallback((id: string) => {\n componentsRef.current.delete(id);\n }, []),\n\n get: useCallback((id: string) => {\n return componentsRef.current.get(id);\n }, []),\n\n has: useCallback((id: string) => {\n return componentsRef.current.has(id);\n }, []),\n\n getRegisteredIds: useCallback(() => {\n return Array.from(componentsRef.current.keys());\n }, []),\n };\n\n return (\n <ComponentRegistryContext.Provider value={registry}>\n {children}\n </ComponentRegistryContext.Provider>\n );\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const context = useContext(ComponentRegistryContext);\n if (!context) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { 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 <Component documentId={documentId} {...(props || {})} />;\n },\n };\n}\n","import { createContext, useContext } from '@framework';\nimport type { ReactNode } from '@framework';\nimport { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RenderersContext = createContext<UIRenderers | null>(null);\n\nexport interface RenderersProviderProps {\n children: ReactNode;\n renderers: UIRenderers;\n}\n\nexport function RenderersProvider({ children, renderers }: RenderersProviderProps) {\n return <RenderersContext.Provider value={renderers}>{children}</RenderersContext.Provider>;\n}\n\nexport function useRenderers(): UIRenderers {\n const context = useContext(RenderersContext);\n if (!context) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return context;\n}\n","import { useUICapability, useUIState } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * High-level hook for rendering UI from schema\n *\n * Provides simple functions to render toolbars and panels by placement+slot.\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 */\nexport function useSchemaRenderer(documentId: string) {\n const renderers = useRenderers();\n const { provides } = useUICapability();\n const schema = provides?.getSchema();\n const uiState = useUIState(documentId); // Subscribe to state changes\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 * ```tsx\n * {renderToolbar('top', 'main')}\n * {renderToolbar('top', 'secondary')}\n * ```\n */\n renderToolbar: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.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.forDocument(documentId).closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n const ToolbarRenderer = renderers.toolbar;\n\n // ALWAYS render, pass isOpen state\n return (\n <ToolbarRenderer\n key={toolbarSlot.toolbarId}\n schema={toolbarSchema}\n documentId={documentId}\n isOpen={toolbarSlot.isOpen}\n onClose={handleClose}\n />\n );\n },\n\n /**\n * Render a panel by placement and slot\n *\n * ALWAYS renders (when panel 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 * ```tsx\n * {renderPanel('left', 'main')}\n * {renderPanel('right', 'main')}\n * ```\n */\n renderPanel: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n const slotKey = `${placement}-${slot}`;\n const panelSlot = uiState.activePanels[slotKey];\n\n // If no panel in this slot, nothing to render\n if (!panelSlot) return null;\n\n const panelSchema = schema.panels[panelSlot.panelId];\n if (!panelSchema) {\n console.warn(`Panel \"${panelSlot.panelId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n provides.forDocument(documentId).closePanelSlot(placement, slot);\n };\n\n const PanelRenderer = renderers.panel;\n\n // ALWAYS render, pass isOpen state\n // Your renderer decides whether to return null or animate\n return (\n <PanelRenderer\n key={panelSlot.panelId}\n schema={panelSchema}\n documentId={documentId}\n isOpen={panelSlot.isOpen}\n onClose={handleClose}\n />\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) return [];\n return Object.entries(uiState.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 panels for this document\n * Useful for batch rendering or debugging\n */\n getActivePanels: () => {\n if (!uiState) return [];\n return Object.entries(uiState.activePanels).map(([slotKey, panelSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n panelId: panelSlot.panelId,\n isOpen: panelSlot.isOpen,\n };\n });\n },\n };\n}\n","import { useCallback } from '@framework';\nimport { SelectionMenuPropsBase, SelectionMenuRenderFn } from '@embedpdf/utils/@framework';\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\n * @returns A render function compatible with layer selectionMenu props\n */\nexport function useSelectionMenu<TContext extends { type: string }>(\n menuId: string,\n documentId: string,\n): SelectionMenuRenderFn<TContext> | undefined {\n const { provides } = useUICapability();\n const renderers = useRenderers();\n\n const renderFn = useCallback(\n (props: SelectionMenuPropsBase<TContext>) => {\n const schema = provides?.getSchema();\n const menuSchema = schema?.selectionMenus?.[menuId];\n\n if (!menuSchema) {\n return null;\n }\n\n if (!props.selected) {\n return null;\n }\n\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n return <SelectionMenuRenderer schema={menuSchema} documentId={documentId} props={props} />;\n },\n [provides, renderers, menuId, documentId],\n );\n\n // Return undefined if schema doesn't have this menu\n const schema = provides?.getSchema();\n if (!schema?.selectionMenus?.[menuId]) {\n return undefined;\n }\n\n return renderFn;\n}\n","import { useState, useEffect } from '@framework';\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 */\nexport interface AutoMenuRendererProps {\n container?: HTMLElement | null;\n documentId: string; // Which document's menus to render\n}\n\nexport function AutoMenuRenderer({ container, documentId }: AutoMenuRendererProps) {\n const uiState = useUIState(documentId);\n const { provides } = useUICapability();\n const anchorRegistry = useAnchorRegistry();\n const renderers = useRenderers();\n\n const [activeMenu, setActiveMenu] = useState<{\n menuId: string;\n anchorEl: HTMLElement | null;\n } | null>(null);\n\n const openMenus = uiState?.openMenus || {};\n const schema = provides?.getSchema();\n\n // Update active menu when state changes\n useEffect(() => {\n const openMenuIds = Object.keys(openMenus);\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 setActiveMenu(null);\n return;\n }\n\n const menuState = openMenus[menuId];\n if (menuState && menuState.triggeredByItemId) {\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(documentId, menuState.triggeredByItemId);\n setActiveMenu({ menuId, anchorEl: anchor });\n } else {\n setActiveMenu(null);\n }\n } else {\n setActiveMenu(null);\n }\n }, [openMenus, anchorRegistry, documentId]);\n\n const handleClose = () => {\n if (activeMenu) {\n provides?.forDocument(documentId).closeMenu(activeMenu.menuId);\n }\n };\n\n if (!activeMenu || !schema) {\n return null;\n }\n\n const menuSchema = schema.menus[activeMenu.menuId];\n if (!menuSchema) {\n console.warn(`Menu \"${activeMenu.menuId}\" not found in schema`);\n return null;\n }\n\n // Use the user-provided menu renderer\n const MenuRenderer = renderers.menu;\n\n return (\n <MenuRenderer\n schema={menuSchema}\n documentId={documentId}\n anchorEl={activeMenu.anchorEl}\n onClose={handleClose}\n container={container}\n />\n );\n}\n","import { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\nimport { useUICapability, useUIPlugin } from './hooks/use-ui';\nimport {\n useState,\n useEffect,\n useRef,\n useMemo,\n useCallback,\n ReactNode,\n HTMLAttributes,\n} from '@framework';\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\ninterface UIRootProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n}\n\n/**\n * Internal component that handles:\n * 1. Injecting the generated stylesheet (into shadow root or document.head)\n * 2. Managing the data-disabled-categories attribute\n * 3. Updating styles on locale changes\n */\nexport function UIRoot({ children, ...restProps }: UIRootProps) {\n const { plugin } = useUIPlugin();\n const { provides } = useUICapability();\n const [disabledCategories, setDisabledCategories] = useState<string[]>([]);\n const styleElRef = useRef<HTMLStyleElement | null>(null);\n const styleTargetRef = useRef<HTMLElement | ShadowRoot | null>(null);\n const previousElementRef = useRef<HTMLDivElement | null>(null);\n\n // Callback ref that handles style injection when element mounts\n // Handles React Strict Mode by tracking previous element\n const rootRefCallback = useCallback(\n (element: HTMLDivElement | null) => {\n const previousElement = previousElementRef.current;\n\n // Update ref\n previousElementRef.current = element;\n\n // If element is null (unmount), don't do anything yet\n // React Strict Mode will remount, so we'll handle cleanup in useEffect\n if (!element) {\n return;\n }\n\n // If element changed (or is new) and plugin is available, inject styles\n if (element !== previousElement && plugin) {\n const styleTarget = getStyleTarget(element);\n styleTargetRef.current = styleTarget;\n\n // Check if styles already exist in this target\n const existingStyle = styleTarget.querySelector(\n UI_SELECTORS.STYLES,\n ) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleElRef.current = existingStyle;\n // Update content in case locale changed\n existingStyle.textContent = plugin.getStylesheet();\n return;\n }\n\n // Create and inject stylesheet\n const stylesheet = plugin.getStylesheet();\n const styleEl = document.createElement('style');\n styleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n styleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n // For shadow root, prepend before other content\n styleTarget.insertBefore(styleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(styleEl);\n }\n\n styleElRef.current = styleEl;\n }\n },\n [plugin],\n );\n\n // Cleanup on actual unmount (not Strict Mode remount)\n useEffect(() => {\n return () => {\n // Only cleanup if we're actually unmounting (not just Strict Mode)\n // The style element will be reused if component remounts\n if (styleElRef.current?.parentNode && !previousElementRef.current) {\n styleElRef.current.remove();\n }\n styleElRef.current = null;\n styleTargetRef.current = null;\n };\n }, []);\n\n // Subscribe to stylesheet invalidation (locale changes, schema merges)\n useEffect(() => {\n if (!plugin) return;\n\n return plugin.onStylesheetInvalidated(() => {\n // Update the style element content\n if (styleElRef.current) {\n styleElRef.current.textContent = plugin.getStylesheet();\n }\n });\n }, [plugin]);\n\n // Subscribe to category changes\n useEffect(() => {\n if (!provides) return;\n\n setDisabledCategories(provides.getDisabledCategories());\n\n return provides.onCategoryChanged(({ disabledCategories }) => {\n setDisabledCategories(disabledCategories);\n });\n }, [provides]);\n\n // Build the disabled categories attribute value\n const disabledCategoriesAttr = useMemo(\n () => (disabledCategories.length > 0 ? disabledCategories.join(' ') : undefined),\n [disabledCategories],\n );\n\n const rootProps = {\n [UI_ATTRIBUTES.ROOT]: '',\n [UI_ATTRIBUTES.DISABLED_CATEGORIES]: disabledCategoriesAttr,\n };\n\n return (\n <div\n ref={rootRefCallback}\n {...rootProps}\n {...restProps}\n style={{ containerType: 'inline-size', ...restProps.style }}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode, ComponentType, HTMLAttributes } from '@framework';\nimport { AnchorRegistryProvider } from './registries/anchor-registry';\nimport { ComponentRegistryProvider } from './registries/component-registry';\nimport { RenderersProvider } from './registries/renderers-registry';\nimport { BaseComponentProps, UIRenderers } from './types';\nimport { AutoMenuRenderer } from './auto-menu-renderer';\nimport { UIRoot } from './root';\n\n/**\n * UIProvider Props\n */\nexport interface UIProviderProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\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?: Record<string, ComponentType<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\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 * ```tsx\n * <EmbedPDF engine={engine} plugins={plugins}>\n * {({ pluginsReady }) => (\n * pluginsReady && (\n * <DocumentContext>\n * {({ activeDocumentId }) => (\n * 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 * <ViewerLayout />\n * </UIProvider>\n * )\n * )}\n * </DocumentContext>\n * )\n * )}\n * </EmbedPDF>\n * ```\n */\nexport function UIProvider({\n children,\n documentId,\n components = {},\n renderers,\n menuContainer,\n ...restProps\n}: UIProviderProps) {\n return (\n <AnchorRegistryProvider>\n <ComponentRegistryProvider initialComponents={components}>\n <RenderersProvider renderers={renderers}>\n <UIRoot {...restProps}>\n {children}\n {/* Automatically render menus for this document */}\n <AutoMenuRenderer documentId={documentId} container={menuContainer} />\n </UIRoot>\n </RenderersProvider>\n </ComponentRegistryProvider>\n </AnchorRegistryProvider>\n );\n}\n"],"names":["schema","disabledCategories"],"mappings":";;;;;;AAKO,MAAM,kBAAkB,MAAM,cAAwB,SAAS,EAAE;AACjE,MAAM,cAAc,MAAM,UAAoB,SAAS,EAAE;AAKzD,MAAM,aAAa,CAAC,eAAuB;AAChD,QAAM,EAAE,SAAA,IAAa,gBAAA;AACrB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAiC,IAAI;AAE/D,YAAU,MAAM;AACd,QAAI,CAAC,SAAU;AAEf,UAAM,QAAQ,SAAS,YAAY,UAAU;AAC7C,aAAS,MAAM,UAAU;AAGzB,UAAM,eAAe,MAAM,iBAAiB,MAAM,SAAS,MAAM,SAAA,CAAU,CAAC;AAC5E,UAAM,aAAa,MAAM,eAAe,MAAM,SAAS,MAAM,SAAA,CAAU,CAAC;AACxE,UAAM,aAAa,MAAM,eAAe,MAAM,SAAS,MAAM,SAAA,CAAU,CAAC;AACxE,UAAM,YAAY,MAAM,cAAc,MAAM,SAAS,MAAM,SAAA,CAAU,CAAC;AAEtE,WAAO,MAAM;AACX,mBAAA;AACA,iBAAA;AACA,iBAAA;AACA,gBAAA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,UAAU,UAAU,CAAC;AAEzB,SAAO;AACT;AAKO,MAAM,cAAc,MAAuB;AAChD,QAAM,EAAE,SAAA,IAAa,gBAAA;AACrB,UAAO,qCAAU,gBAAe;AAClC;AC7BA,MAAM,wBAAwB,cAAqC,IAAI;AAEhE,SAAS,uBAAuB,EAAE,YAAqC;AAC5E,QAAM,aAAa,OAAiC,oBAAI,KAAK;AAE7D,QAAM,WAA2B;AAAA,IAC/B,UAAU,YAAY,CAAC,YAAoB,QAAgB,YAAyB;AAClF,YAAM,MAAM,GAAG,UAAU,IAAI,MAAM;AACnC,iBAAW,QAAQ,IAAI,KAAK,OAAO;AAAA,IACrC,GAAG,CAAA,CAAE;AAAA,IAEL,YAAY,YAAY,CAAC,YAAoB,WAAmB;AAC9D,YAAM,MAAM,GAAG,UAAU,IAAI,MAAM;AACnC,iBAAW,QAAQ,OAAO,GAAG;AAAA,IAC/B,GAAG,CAAA,CAAE;AAAA,IAEL,WAAW,YAAY,CAAC,YAAoB,WAAmB;AAC7D,YAAM,MAAM,GAAG,UAAU,IAAI,MAAM;AACnC,aAAO,WAAW,QAAQ,IAAI,GAAG,KAAK;AAAA,IACxC,GAAG,CAAA,CAAE;AAAA,EAAA;AAGP,6BACG,sBAAsB,UAAtB,EAA+B,OAAO,UAAW,UAAS;AAE/D;AAEO,SAAS,oBAAoC;AAClD,QAAM,UAAU,WAAW,qBAAqB;AAChD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,SAAO;AACT;AC7BO,SAAS,kBACd,YACA,QACuC;AACvC,QAAM,WAAW,kBAAA;AACjB,QAAM,aAAa,OAA2B,IAAI;AAClD,QAAM,gBAAgB,OAAO,UAAU;AACvC,QAAM,YAAY,OAAO,MAAM;AAG/B,gBAAc,UAAU;AACxB,YAAU,UAAU;AAGpB,SAAO;AAAA,IACL,CAAC,YAAgC;AAE/B,YAAM,kBAAkB,WAAW;AAGnC,iBAAW,UAAU;AAGrB,UAAI,SAAS;AAEX,YAAI,YAAY,iBAAiB;AAC/B,mBAAS,SAAS,cAAc,SAAS,UAAU,SAAS,OAAO;AAAA,QACrE;AAAA,MACF,WAAW,iBAAiB;AAE1B,iBAAS,WAAW,cAAc,SAAS,UAAU,OAAO;AAAA,MAC9D;AAAA,IACF;AAAA,IACA,CAAC,QAAQ;AAAA;AAAA,EAAA;AAEb;ACrCA,MAAM,2BAA2B,cAAwC,IAAI;AAOtE,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA,oBAAoB,CAAA;AACtB,GAAmC;AACjC,QAAM,gBAAgB;AAAA,IACpB,IAAI,IAAI,OAAO,QAAQ,iBAAiB,CAAC;AAAA,EAAA;AAG3C,QAAM,WAA8B;AAAA,IAClC,UAAU,YAAY,CAAC,IAAY,cAAiD;AAClF,oBAAc,QAAQ,IAAI,IAAI,SAAS;AAAA,IACzC,GAAG,CAAA,CAAE;AAAA,IAEL,YAAY,YAAY,CAAC,OAAe;AACtC,oBAAc,QAAQ,OAAO,EAAE;AAAA,IACjC,GAAG,CAAA,CAAE;AAAA,IAEL,KAAK,YAAY,CAAC,OAAe;AAC/B,aAAO,cAAc,QAAQ,IAAI,EAAE;AAAA,IACrC,GAAG,CAAA,CAAE;AAAA,IAEL,KAAK,YAAY,CAAC,OAAe;AAC/B,aAAO,cAAc,QAAQ,IAAI,EAAE;AAAA,IACrC,GAAG,CAAA,CAAE;AAAA,IAEL,kBAAkB,YAAY,MAAM;AAClC,aAAO,MAAM,KAAK,cAAc,QAAQ,MAAM;AAAA,IAChD,GAAG,CAAA,CAAE;AAAA,EAAA;AAGP,6BACG,yBAAyB,UAAzB,EAAkC,OAAO,UACvC,UACH;AAEJ;AAEO,SAAS,uBAA0C;AACxD,QAAM,UAAU,WAAW,wBAAwB;AACnD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AC9DO,SAAS,kBAAkB;AAChC,QAAM,oBAAoB,qBAAA;AAE1B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASL,uBAAuB,CAAC,aAAqB,YAAoB,UAAgB;AAC/E,YAAM,YAAY,kBAAkB,IAAI,WAAW;AAEnD,UAAI,CAAC,WAAW;AACd,gBAAQ,MAAM,cAAc,WAAW,yBAAyB;AAChE,eAAO;AAAA,MACT;AAEA,iCAAQ,WAAA,EAAU,YAAyB,GAAI,SAAS,CAAA,GAAK;AAAA,IAC/D;AAAA,EAAA;AAEJ;ACnBA,MAAM,mBAAmB,cAAkC,IAAI;AAOxD,SAAS,kBAAkB,EAAE,UAAU,aAAqC;AACjF,6BAAQ,iBAAiB,UAAjB,EAA0B,OAAO,WAAY,UAAS;AAChE;AAEO,SAAS,eAA4B;AAC1C,QAAM,UAAU,WAAW,gBAAgB;AAC3C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,SAAO;AACT;ACfO,SAAS,kBAAkB,YAAoB;AACpD,QAAM,YAAY,aAAA;AAClB,QAAM,EAAE,SAAA,IAAa,gBAAA;AACrB,QAAM,SAAS,qCAAU;AACzB,QAAM,UAAU,WAAW,UAAU;AAErC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeL,eAAe,CAAC,WAAgD,SAAiB;AAC/E,UAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAS,QAAO;AAE7C,YAAM,UAAU,GAAG,SAAS,IAAI,IAAI;AACpC,YAAM,cAAc,QAAQ,eAAe,OAAO;AAGlD,UAAI,CAAC,YAAa,QAAO;AAEzB,YAAM,gBAAgB,OAAO,SAAS,YAAY,SAAS;AAC3D,UAAI,CAAC,eAAe;AAClB,gBAAQ,KAAK,YAAY,YAAY,SAAS,uBAAuB;AACrE,eAAO;AAAA,MACT;AAGA,YAAM,aAAa,CAAC,cAAc;AAElC,YAAM,cAAc,aAChB,MAAM;AACJ,iBAAS,YAAY,UAAU,EAAE,iBAAiB,WAAW,IAAI;AAAA,MACnE,IACA;AAEJ,YAAM,kBAAkB,UAAU;AAGlC,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,QAAQ;AAAA,UACR;AAAA,UACA,QAAQ,YAAY;AAAA,UACpB,SAAS;AAAA,QAAA;AAAA,QAJJ,YAAY;AAAA,MAAA;AAAA,IAOvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiBA,aAAa,CAAC,WAAgD,SAAiB;AAC7E,UAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAS,QAAO;AAC7C,YAAM,UAAU,GAAG,SAAS,IAAI,IAAI;AACpC,YAAM,YAAY,QAAQ,aAAa,OAAO;AAG9C,UAAI,CAAC,UAAW,QAAO;AAEvB,YAAM,cAAc,OAAO,OAAO,UAAU,OAAO;AACnD,UAAI,CAAC,aAAa;AAChB,gBAAQ,KAAK,UAAU,UAAU,OAAO,uBAAuB;AAC/D,eAAO;AAAA,MACT;AAEA,YAAM,cAAc,MAAM;AACxB,iBAAS,YAAY,UAAU,EAAE,eAAe,WAAW,IAAI;AAAA,MACjE;AAEA,YAAM,gBAAgB,UAAU;AAIhC,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,QAAQ;AAAA,UACR;AAAA,UACA,QAAQ,UAAU;AAAA,UAClB,SAAS;AAAA,QAAA;AAAA,QAJJ,UAAU;AAAA,MAAA;AAAA,IAOrB;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,mBAAmB,MAAM;AACvB,UAAI,CAAC,QAAS,QAAO,CAAA;AACrB,aAAO,OAAO,QAAQ,QAAQ,cAAc,EAAE,IAAI,CAAC,CAAC,SAAS,WAAW,MAAM;AAC5E,cAAM,CAAC,WAAW,IAAI,IAAI,QAAQ,MAAM,GAAG;AAC3C,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA,WAAW,YAAY;AAAA,UACvB,QAAQ,YAAY;AAAA,QAAA;AAAA,MAExB,CAAC;AAAA,IACH;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,iBAAiB,MAAM;AACrB,UAAI,CAAC,QAAS,QAAO,CAAA;AACrB,aAAO,OAAO,QAAQ,QAAQ,YAAY,EAAE,IAAI,CAAC,CAAC,SAAS,SAAS,MAAM;AACxE,cAAM,CAAC,WAAW,IAAI,IAAI,QAAQ,MAAM,GAAG;AAC3C,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA,SAAS,UAAU;AAAA,UACnB,QAAQ,UAAU;AAAA,QAAA;AAAA,MAEtB,CAAC;AAAA,IACH;AAAA,EAAA;AAEJ;AC5IO,SAAS,iBACd,QACA,YAC6C;;AAC7C,QAAM,EAAE,SAAA,IAAa,gBAAA;AACrB,QAAM,YAAY,aAAA;AAElB,QAAM,WAAW;AAAA,IACf,CAAC,UAA4C;;AAC3C,YAAMA,UAAS,qCAAU;AACzB,YAAM,cAAaA,MAAAA,mCAAQ,mBAARA,gBAAAA,IAAyB;AAE5C,UAAI,CAAC,YAAY;AACf,eAAO;AAAA,MACT;AAEA,UAAI,CAAC,MAAM,UAAU;AACnB,eAAO;AAAA,MACT;AAEA,YAAM,wBAAwB,UAAU;AAExC,aAAO,oBAAC,uBAAA,EAAsB,QAAQ,YAAY,YAAwB,OAAc;AAAA,IAC1F;AAAA,IACA,CAAC,UAAU,WAAW,QAAQ,UAAU;AAAA,EAAA;AAI1C,QAAM,SAAS,qCAAU;AACzB,MAAI,GAAC,sCAAQ,mBAAR,mBAAyB,UAAS;AACrC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AC5BO,SAAS,iBAAiB,EAAE,WAAW,cAAqC;AACjF,QAAM,UAAU,WAAW,UAAU;AACrC,QAAM,EAAE,SAAA,IAAa,gBAAA;AACrB,QAAM,iBAAiB,kBAAA;AACvB,QAAM,YAAY,aAAA;AAElB,QAAM,CAAC,YAAY,aAAa,IAAI,SAG1B,IAAI;AAEd,QAAM,aAAY,mCAAS,cAAa,CAAA;AACxC,QAAM,SAAS,qCAAU;AAGzB,YAAU,MAAM;AACd,UAAM,cAAc,OAAO,KAAK,SAAS;AAEzC,QAAI,YAAY,SAAS,GAAG;AAE1B,YAAM,SAAS,YAAY,CAAC;AAC5B,UAAI,CAAC,QAAQ;AACX,sBAAc,IAAI;AAClB;AAAA,MACF;AAEA,YAAM,YAAY,UAAU,MAAM;AAClC,UAAI,aAAa,UAAU,mBAAmB;AAE5C,cAAM,SAAS,eAAe,UAAU,YAAY,UAAU,iBAAiB;AAC/E,sBAAc,EAAE,QAAQ,UAAU,OAAA,CAAQ;AAAA,MAC5C,OAAO;AACL,sBAAc,IAAI;AAAA,MACpB;AAAA,IACF,OAAO;AACL,oBAAc,IAAI;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,WAAW,gBAAgB,UAAU,CAAC;AAE1C,QAAM,cAAc,MAAM;AACxB,QAAI,YAAY;AACd,2CAAU,YAAY,YAAY,UAAU,WAAW;AAAA,IACzD;AAAA,EACF;AAEA,MAAI,CAAC,cAAc,CAAC,QAAQ;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,OAAO,MAAM,WAAW,MAAM;AACjD,MAAI,CAAC,YAAY;AACf,YAAQ,KAAK,SAAS,WAAW,MAAM,uBAAuB;AAC9D,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,UAAU;AAE/B,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,QAAQ;AAAA,MACR;AAAA,MACA,UAAU,WAAW;AAAA,MACrB,SAAS;AAAA,MACT;AAAA,IAAA;AAAA,EAAA;AAGN;ACrEA,SAAS,eAAe,SAAgD;AACtE,QAAM,OAAO,QAAQ,YAAA;AACrB,MAAI,gBAAgB,YAAY;AAC9B,WAAO;AAAA,EACT;AACA,SAAO,SAAS;AAClB;AAYO,SAAS,OAAO,EAAE,UAAU,GAAG,aAA0B;AAC9D,QAAM,EAAE,OAAA,IAAW,YAAA;AACnB,QAAM,EAAE,SAAA,IAAa,gBAAA;AACrB,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAmB,CAAA,CAAE;AACzE,QAAM,aAAa,OAAgC,IAAI;AACvD,QAAM,iBAAiB,OAAwC,IAAI;AACnE,QAAM,qBAAqB,OAA8B,IAAI;AAI7D,QAAM,kBAAkB;AAAA,IACtB,CAAC,YAAmC;AAClC,YAAM,kBAAkB,mBAAmB;AAG3C,yBAAmB,UAAU;AAI7B,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAGA,UAAI,YAAY,mBAAmB,QAAQ;AACzC,cAAM,cAAc,eAAe,OAAO;AAC1C,uBAAe,UAAU;AAGzB,cAAM,gBAAgB,YAAY;AAAA,UAChC,aAAa;AAAA,QAAA;AAGf,YAAI,eAAe;AACjB,qBAAW,UAAU;AAErB,wBAAc,cAAc,OAAO,cAAA;AACnC;AAAA,QACF;AAGA,cAAM,aAAa,OAAO,cAAA;AAC1B,cAAM,UAAU,SAAS,cAAc,OAAO;AAC9C,gBAAQ,aAAa,cAAc,QAAQ,EAAE;AAC7C,gBAAQ,cAAc;AAEtB,YAAI,uBAAuB,YAAY;AAErC,sBAAY,aAAa,SAAS,YAAY,UAAU;AAAA,QAC1D,OAAO;AACL,sBAAY,YAAY,OAAO;AAAA,QACjC;AAEA,mBAAW,UAAU;AAAA,MACvB;AAAA,IACF;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAIT,YAAU,MAAM;AACd,WAAO,MAAM;;AAGX,YAAI,gBAAW,YAAX,mBAAoB,eAAc,CAAC,mBAAmB,SAAS;AACjE,mBAAW,QAAQ,OAAA;AAAA,MACrB;AACA,iBAAW,UAAU;AACrB,qBAAe,UAAU;AAAA,IAC3B;AAAA,EACF,GAAG,CAAA,CAAE;AAGL,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAEb,WAAO,OAAO,wBAAwB,MAAM;AAE1C,UAAI,WAAW,SAAS;AACtB,mBAAW,QAAQ,cAAc,OAAO,cAAA;AAAA,MAC1C;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,QAAI,CAAC,SAAU;AAEf,0BAAsB,SAAS,uBAAuB;AAEtD,WAAO,SAAS,kBAAkB,CAAC,EAAE,oBAAAC,0BAAyB;AAC5D,4BAAsBA,mBAAkB;AAAA,IAC1C,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAGb,QAAM,yBAAyB;AAAA,IAC7B,MAAO,mBAAmB,SAAS,IAAI,mBAAmB,KAAK,GAAG,IAAI;AAAA,IACtE,CAAC,kBAAkB;AAAA,EAAA;AAGrB,QAAM,YAAY;AAAA,IAChB,CAAC,cAAc,IAAI,GAAG;AAAA,IACtB,CAAC,cAAc,mBAAmB,GAAG;AAAA,EAAA;AAGvC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK;AAAA,MACJ,GAAG;AAAA,MACH,GAAG;AAAA,MACJ,OAAO,EAAE,eAAe,eAAe,GAAG,UAAU,MAAA;AAAA,MAEnD;AAAA,IAAA;AAAA,EAAA;AAGP;ACxEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA,aAAa,CAAA;AAAA,EACb;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE,oBAAC,wBAAA,EACC,UAAA,oBAAC,2BAAA,EAA0B,mBAAmB,YAC5C,UAAA,oBAAC,mBAAA,EAAkB,WACjB,UAAA,qBAAC,QAAA,EAAQ,GAAG,WACT,UAAA;AAAA,IAAA;AAAA,IAED,oBAAC,kBAAA,EAAiB,YAAwB,WAAW,cAAA,CAAe;AAAA,EAAA,GACtE,EAAA,CACF,GACF,GACF;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-ui.ts","../../src/shared/hooks/use-ui-container.ts","../../src/shared/registries/anchor-registry.tsx","../../src/shared/hooks/use-register-anchor.ts","../../src/shared/registries/component-registry.tsx","../../src/shared/hooks/use-item-renderer.tsx","../../src/shared/registries/renderers-registry.tsx","../../src/shared/hooks/use-schema-renderer.tsx","../../src/shared/hooks/use-selection-menu.tsx","../../src/shared/auto-menu-renderer.tsx","../../src/shared/root.tsx","../../src/shared/provider.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { UIPlugin } from '@embedpdf/plugin-ui';\nimport { useState, useEffect } from '@framework';\nimport { UIDocumentState, UISchema } from '@embedpdf/plugin-ui';\n\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\n\n/**\n * Get UI state for a document\n */\nexport const useUIState = (documentId: string) => {\n const { provides } = useUICapability();\n const [state, setState] = useState<UIDocumentState | null>(null);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n setState(scope.getState());\n\n // Subscribe to changes\n const unsubToolbar = scope.onToolbarChanged(() => setState(scope.getState()));\n const unsubSidebar = scope.onSidebarChanged(() => setState(scope.getState()));\n const unsubModal = scope.onModalChanged(() => setState(scope.getState()));\n const unsubMenu = scope.onMenuChanged(() => setState(scope.getState()));\n\n return () => {\n unsubToolbar();\n unsubSidebar();\n unsubModal();\n unsubMenu();\n };\n }, [provides, documentId]);\n\n return state;\n};\n\n/**\n * Get UI schema\n */\nexport const useUISchema = (): UISchema | null => {\n const { provides } = useUICapability();\n return provides?.getSchema() ?? null;\n};\n","import { createContext, useContext, RefObject } from '@framework';\n\nexport interface UIContainerContextValue {\n /** Reference to the UIRoot container element */\n containerRef: RefObject<HTMLDivElement>;\n /** Get the container element (may be null if not mounted) */\n getContainer: () => HTMLDivElement | null;\n}\n\nexport const UIContainerContext = createContext<UIContainerContextValue | null>(null);\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 * ```tsx\n * function MyComponent() {\n * const { containerRef, getContainer } = useUIContainer();\n *\n * // Use containerRef for ResizeObserver\n * useEffect(() => {\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 * return () => observer.disconnect();\n * }, [getContainer]);\n *\n * // Or portal to the container\n * return createPortal(<Modal />, getContainer()!);\n * }\n * ```\n */\nexport function useUIContainer(): UIContainerContextValue {\n const context = useContext(UIContainerContext);\n if (!context) {\n throw new Error('useUIContainer must be used within a UIProvider');\n }\n return context;\n}\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ReactNode } from '@framework';\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 AnchorRegistryContext = createContext<AnchorRegistry | null>(null);\n\nexport function AnchorRegistryProvider({ children }: { children: ReactNode }) {\n const anchorsRef = useRef<Map<string, HTMLElement>>(new Map());\n\n const registry: AnchorRegistry = {\n register: useCallback((documentId: string, itemId: string, element: HTMLElement) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.set(key, element);\n }, []),\n\n unregister: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.delete(key);\n }, []),\n\n getAnchor: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n return anchorsRef.current.get(key) || null;\n }, []),\n };\n\n return (\n <AnchorRegistryContext.Provider value={registry}>{children}</AnchorRegistryContext.Provider>\n );\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const context = useContext(AnchorRegistryContext);\n if (!context) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { useCallback, useRef } from '@framework';\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 callback to attach to the element\n *\n * @example\n * ```tsx\n * function ZoomButton({ documentId }: Props) {\n * const anchorRef = useRegisterAnchor(documentId, 'zoom-button');\n *\n * return <button ref={anchorRef}>Zoom</button>;\n * }\n * ```\n */\nexport function useRegisterAnchor(\n documentId: string,\n itemId: string,\n): (element: HTMLElement | null) => void {\n const registry = useAnchorRegistry();\n const elementRef = useRef<HTMLElement | null>(null);\n const documentIdRef = useRef(documentId);\n const itemIdRef = useRef(itemId);\n\n // Keep refs in sync\n documentIdRef.current = documentId;\n itemIdRef.current = itemId;\n\n // Return stable callback that uses refs\n return useCallback(\n (element: HTMLElement | null) => {\n // Store previous element\n const previousElement = elementRef.current;\n\n // Update ref\n elementRef.current = element;\n\n // Handle registration/unregistration\n if (element) {\n // Register new element\n if (element !== previousElement) {\n registry.register(documentIdRef.current, itemIdRef.current, element);\n }\n } else if (previousElement) {\n // Element is now null, but we had one before - unregister\n registry.unregister(documentIdRef.current, itemIdRef.current);\n }\n },\n [registry], // Only depend on registry!\n );\n}\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ComponentType, ReactNode } from '@framework';\nimport { 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: ComponentType<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): ComponentType<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst ComponentRegistryContext = createContext<ComponentRegistry | null>(null);\n\nexport interface ComponentRegistryProviderProps {\n children: ReactNode;\n initialComponents?: Record<string, ComponentType<BaseComponentProps>>;\n}\n\nexport function ComponentRegistryProvider({\n children,\n initialComponents = {},\n}: ComponentRegistryProviderProps) {\n const componentsRef = useRef<Map<string, ComponentType<BaseComponentProps>>>(\n new Map(Object.entries(initialComponents)),\n );\n\n const registry: ComponentRegistry = {\n register: useCallback((id: string, component: ComponentType<BaseComponentProps>) => {\n componentsRef.current.set(id, component);\n }, []),\n\n unregister: useCallback((id: string) => {\n componentsRef.current.delete(id);\n }, []),\n\n get: useCallback((id: string) => {\n return componentsRef.current.get(id);\n }, []),\n\n has: useCallback((id: string) => {\n return componentsRef.current.has(id);\n }, []),\n\n getRegisteredIds: useCallback(() => {\n return Array.from(componentsRef.current.keys());\n }, []),\n };\n\n return (\n <ComponentRegistryContext.Provider value={registry}>\n {children}\n </ComponentRegistryContext.Provider>\n );\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const context = useContext(ComponentRegistryContext);\n if (!context) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { 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 <Component documentId={documentId} {...(props || {})} />;\n },\n };\n}\n","import { createContext, useContext } from '@framework';\nimport type { ReactNode } from '@framework';\nimport { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RenderersContext = createContext<UIRenderers | null>(null);\n\nexport interface RenderersProviderProps {\n children: ReactNode;\n renderers: UIRenderers;\n}\n\nexport function RenderersProvider({ children, renderers }: RenderersProviderProps) {\n return <RenderersContext.Provider value={renderers}>{children}</RenderersContext.Provider>;\n}\n\nexport function useRenderers(): UIRenderers {\n const context = useContext(RenderersContext);\n if (!context) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return context;\n}\n","import { useUICapability, useUIState } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * High-level hook 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 */\nexport function useSchemaRenderer(documentId: string) {\n const renderers = useRenderers();\n const { provides } = useUICapability();\n const schema = provides?.getSchema();\n const uiState = useUIState(documentId); // Subscribe to state changes\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 * ```tsx\n * {renderToolbar('top', 'main')}\n * {renderToolbar('top', 'secondary')}\n * ```\n */\n renderToolbar: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.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.forDocument(documentId).closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n const ToolbarRenderer = renderers.toolbar;\n\n // ALWAYS render, pass isOpen state\n return (\n <ToolbarRenderer\n key={toolbarSlot.toolbarId}\n schema={toolbarSchema}\n documentId={documentId}\n isOpen={toolbarSlot.isOpen}\n onClose={handleClose}\n />\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 * ```tsx\n * {renderSidebar('left', 'main')}\n * {renderSidebar('right', 'main')}\n * ```\n */\n renderSidebar: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n const slotKey = `${placement}-${slot}`;\n const sidebarSlot = uiState.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.forDocument(documentId).closeSidebarSlot(placement, slot);\n };\n\n const SidebarRenderer = renderers.sidebar;\n\n // ALWAYS render, pass isOpen state\n // Your renderer decides whether to return null or animate\n return (\n <SidebarRenderer\n key={sidebarSlot.sidebarId}\n schema={sidebarSchema}\n documentId={documentId}\n isOpen={sidebarSlot.isOpen}\n onClose={handleClose}\n />\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 * ```tsx\n * {renderModal()}\n * ```\n */\n renderModal: () => {\n if (!schema || !provides || !uiState?.activeModal) return null;\n\n const { modalId, isOpen } = uiState.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.forDocument(documentId).closeModal();\n };\n\n const handleExited = () => {\n provides.forDocument(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 (\n <ModalRenderer\n key={modalId}\n schema={modalSchema}\n documentId={documentId}\n isOpen={isOpen}\n onClose={handleClose}\n onExited={handleExited}\n />\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) return [];\n return Object.entries(uiState.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) return [];\n return Object.entries(uiState.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 * ```tsx\n * <div className=\"relative\">\n * {children}\n * {renderOverlays()}\n * </div>\n * ```\n */\n renderOverlays: () => {\n if (!schema?.overlays || !provides) 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 (\n <>\n {overlays.map((overlaySchema) => (\n <OverlayRenderer\n key={overlaySchema.id}\n schema={overlaySchema}\n documentId={documentId}\n />\n ))}\n </>\n );\n },\n };\n}\n","import { useCallback } from '@framework';\nimport { SelectionMenuPropsBase, SelectionMenuRenderFn } from '@embedpdf/utils/@framework';\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\n * @returns A render function compatible with layer selectionMenu props\n */\nexport function useSelectionMenu<TContext extends { type: string }>(\n menuId: string,\n documentId: string,\n): SelectionMenuRenderFn<TContext> | undefined {\n const { provides } = useUICapability();\n const renderers = useRenderers();\n\n const renderFn = useCallback(\n (props: SelectionMenuPropsBase<TContext>) => {\n const schema = provides?.getSchema();\n const menuSchema = schema?.selectionMenus?.[menuId];\n\n if (!menuSchema) {\n return null;\n }\n\n if (!props.selected) {\n return null;\n }\n\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n return <SelectionMenuRenderer schema={menuSchema} documentId={documentId} props={props} />;\n },\n [provides, renderers, menuId, documentId],\n );\n\n // Return undefined if schema doesn't have this menu\n const schema = provides?.getSchema();\n if (!schema?.selectionMenus?.[menuId]) {\n return undefined;\n }\n\n return renderFn;\n}\n","import { useState, useEffect } from '@framework';\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 */\nexport interface AutoMenuRendererProps {\n container?: HTMLElement | null;\n documentId: string; // Which document's menus to render\n}\n\nexport function AutoMenuRenderer({ container, documentId }: AutoMenuRendererProps) {\n const uiState = useUIState(documentId);\n const { provides } = useUICapability();\n const anchorRegistry = useAnchorRegistry();\n const renderers = useRenderers();\n\n const [activeMenu, setActiveMenu] = useState<{\n menuId: string;\n anchorEl: HTMLElement | null;\n } | null>(null);\n\n const openMenus = uiState?.openMenus || {};\n const schema = provides?.getSchema();\n\n // Update active menu when state changes\n useEffect(() => {\n const openMenuIds = Object.keys(openMenus);\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 setActiveMenu(null);\n return;\n }\n\n const menuState = openMenus[menuId];\n if (menuState && menuState.triggeredByItemId) {\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(documentId, menuState.triggeredByItemId);\n setActiveMenu({ menuId, anchorEl: anchor });\n } else {\n setActiveMenu(null);\n }\n } else {\n setActiveMenu(null);\n }\n }, [openMenus, anchorRegistry, documentId]);\n\n const handleClose = () => {\n if (activeMenu) {\n provides?.forDocument(documentId).closeMenu(activeMenu.menuId);\n }\n };\n\n if (!activeMenu || !schema) {\n return null;\n }\n\n const menuSchema = schema.menus[activeMenu.menuId];\n if (!menuSchema) {\n console.warn(`Menu \"${activeMenu.menuId}\" not found in schema`);\n return null;\n }\n\n // Use the user-provided menu renderer\n const MenuRenderer = renderers.menu;\n\n return (\n <MenuRenderer\n schema={menuSchema}\n documentId={documentId}\n anchorEl={activeMenu.anchorEl}\n onClose={handleClose}\n container={container}\n />\n );\n}\n","import { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\nimport { useUICapability, useUIPlugin } from './hooks/use-ui';\nimport { UIContainerContext, UIContainerContextValue } from './hooks/use-ui-container';\nimport {\n useState,\n useEffect,\n useRef,\n useMemo,\n useCallback,\n ReactNode,\n HTMLAttributes,\n} from '@framework';\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\ninterface UIRootProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n}\n\n/**\n * Internal component that handles:\n * 1. Injecting the generated stylesheet (into shadow root or document.head)\n * 2. Managing the data-disabled-categories attribute\n * 3. Updating styles on locale changes\n */\nexport function UIRoot({ children, style, ...restProps }: UIRootProps) {\n const { plugin } = useUIPlugin();\n const { provides } = useUICapability();\n const [disabledCategories, setDisabledCategories] = useState<string[]>([]);\n const [hiddenItems, setHiddenItems] = useState<string[]>([]);\n const styleElRef = useRef<HTMLStyleElement | null>(null);\n const styleTargetRef = useRef<HTMLElement | ShadowRoot | null>(null);\n const previousElementRef = useRef<HTMLDivElement | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Create container context value (memoized to prevent unnecessary re-renders)\n const containerContextValue = useMemo<UIContainerContextValue>(\n () => ({\n containerRef,\n getContainer: () => containerRef.current,\n }),\n [],\n );\n\n // Callback ref that handles style injection when element mounts\n // Handles React Strict Mode by tracking previous element\n const rootRefCallback = useCallback(\n (element: HTMLDivElement | null) => {\n const previousElement = previousElementRef.current;\n\n // Update refs\n previousElementRef.current = element;\n (containerRef as any).current = element;\n\n // If element is null (unmount), don't do anything yet\n // React Strict Mode will remount, so we'll handle cleanup in useEffect\n if (!element) {\n return;\n }\n\n // If element changed (or is new) and plugin is available, inject styles\n if (element !== previousElement && plugin) {\n const styleTarget = getStyleTarget(element);\n styleTargetRef.current = styleTarget;\n\n // Check if styles already exist in this target\n const existingStyle = styleTarget.querySelector(\n UI_SELECTORS.STYLES,\n ) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleElRef.current = existingStyle;\n // Update content in case locale changed\n existingStyle.textContent = plugin.getStylesheet();\n return;\n }\n\n // Create and inject stylesheet\n const stylesheet = plugin.getStylesheet();\n const styleEl = document.createElement('style');\n styleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n styleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n // For shadow root, prepend before other content\n styleTarget.insertBefore(styleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(styleEl);\n }\n\n styleElRef.current = styleEl;\n }\n },\n [plugin],\n );\n\n // Cleanup on actual unmount (not Strict Mode remount)\n useEffect(() => {\n return () => {\n // Only cleanup if we're actually unmounting (not just Strict Mode)\n // The style element will be reused if component remounts\n if (styleElRef.current?.parentNode && !previousElementRef.current) {\n styleElRef.current.remove();\n }\n styleElRef.current = null;\n styleTargetRef.current = null;\n };\n }, []);\n\n // Subscribe to stylesheet invalidation (locale changes, schema merges)\n useEffect(() => {\n if (!plugin) return;\n\n return plugin.onStylesheetInvalidated(() => {\n // Update the style element content\n if (styleElRef.current) {\n styleElRef.current.textContent = plugin.getStylesheet();\n }\n });\n }, [plugin]);\n\n // Subscribe to category and hidden items changes\n useEffect(() => {\n if (!provides) return;\n\n setDisabledCategories(provides.getDisabledCategories());\n setHiddenItems(provides.getHiddenItems());\n\n return provides.onCategoryChanged(({ disabledCategories, hiddenItems }) => {\n setDisabledCategories(disabledCategories);\n setHiddenItems(hiddenItems);\n });\n }, [provides]);\n\n // Build the disabled categories attribute value\n const disabledCategoriesAttr = useMemo(\n () => (disabledCategories.length > 0 ? disabledCategories.join(' ') : undefined),\n [disabledCategories],\n );\n\n // Build the hidden items attribute value\n const hiddenItemsAttr = useMemo(\n () => (hiddenItems.length > 0 ? hiddenItems.join(' ') : undefined),\n [hiddenItems],\n );\n\n const combinedStyle = useMemo(() => {\n const base = { containerType: 'inline-size' as const };\n if (style && typeof style === 'object') {\n return { ...base, ...style };\n }\n return base;\n }, [style]);\n\n const rootProps = {\n [UI_ATTRIBUTES.ROOT]: '',\n [UI_ATTRIBUTES.DISABLED_CATEGORIES]: disabledCategoriesAttr,\n [UI_ATTRIBUTES.HIDDEN_ITEMS]: hiddenItemsAttr,\n };\n\n return (\n <UIContainerContext.Provider value={containerContextValue}>\n <div ref={rootRefCallback} {...rootProps} {...restProps} style={combinedStyle}>\n {children}\n </div>\n </UIContainerContext.Provider>\n );\n}\n","import type { ReactNode, ComponentType, HTMLAttributes } from '@framework';\nimport { AnchorRegistryProvider } from './registries/anchor-registry';\nimport { ComponentRegistryProvider } from './registries/component-registry';\nimport { RenderersProvider } from './registries/renderers-registry';\nimport { BaseComponentProps, UIRenderers } from './types';\nimport { AutoMenuRenderer } from './auto-menu-renderer';\nimport { UIRoot } from './root';\n\n/**\n * UIProvider Props\n */\nexport interface UIProviderProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\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?: Record<string, ComponentType<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\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 * ```tsx\n * <EmbedPDF engine={engine} plugins={plugins}>\n * {({ pluginsReady }) => (\n * pluginsReady && (\n * <DocumentContext>\n * {({ activeDocumentId }) => (\n * 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 * <ViewerLayout />\n * </UIProvider>\n * )\n * )}\n * </DocumentContext>\n * )\n * )}\n * </EmbedPDF>\n * ```\n */\nexport function UIProvider({\n children,\n documentId,\n components = {},\n renderers,\n menuContainer,\n ...restProps\n}: UIProviderProps) {\n return (\n <AnchorRegistryProvider>\n <ComponentRegistryProvider initialComponents={components}>\n <RenderersProvider renderers={renderers}>\n <UIRoot {...restProps}>\n {children}\n {/* Automatically render menus for this document */}\n <AutoMenuRenderer documentId={documentId} container={menuContainer} />\n </UIRoot>\n </RenderersProvider>\n </ComponentRegistryProvider>\n </AnchorRegistryProvider>\n );\n}\n"],"names":["schema","disabledCategories","hiddenItems"],"mappings":"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"}
|
package/dist/react/adapter.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Fragment, useEffect, useRef, useState, useCallback, useMemo, useContext, createContext, } from 'react';
|
|
2
|
-
export type { ReactNode, HTMLAttributes, CSSProperties, ComponentType } from 'react';
|
|
2
|
+
export type { ReactNode, HTMLAttributes, CSSProperties, ComponentType, RefObject } from 'react';
|
package/dist/react/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/react"),n=require("@embedpdf/plugin-ui"),t=require("react"),r=require("react/jsx-runtime"),o=()=>e.useCapability(n.UIPlugin.id),s=()=>e.usePlugin(n.UIPlugin.id),u=e=>{const{provides:n}=o(),[r,s]=t.useState(null);return t.useEffect(()=>{if(!n)return;const t=n.forDocument(e);s(t.getState());const r=t.onToolbarChanged(()=>s(t.getState())),o=t.
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/react"),n=require("@embedpdf/plugin-ui"),t=require("react"),r=require("react/jsx-runtime"),o=()=>e.useCapability(n.UIPlugin.id),s=()=>e.usePlugin(n.UIPlugin.id),u=e=>{const{provides:n}=o(),[r,s]=t.useState(null);return t.useEffect(()=>{if(!n)return;const t=n.forDocument(e);s(t.getState());const r=t.onToolbarChanged(()=>s(t.getState())),o=t.onSidebarChanged(()=>s(t.getState())),u=t.onModalChanged(()=>s(t.getState())),l=t.onMenuChanged(()=>s(t.getState()));return()=>{r(),o(),u(),l()}},[n,e]),r},l=t.createContext(null);const c=t.createContext(null);function i({children:e}){const n=t.useRef(new Map),o={register:t.useCallback((e,t,r)=>{const o=`${e}:${t}`;n.current.set(o,r)},[]),unregister:t.useCallback((e,t)=>{const r=`${e}:${t}`;n.current.delete(r)},[]),getAnchor:t.useCallback((e,t)=>{const r=`${e}:${t}`;return n.current.get(r)||null},[])};return r.jsx(c.Provider,{value:o,children:e})}function d(){const e=t.useContext(c);if(!e)throw new Error("useAnchorRegistry must be used within UIProvider");return e}const a=t.createContext(null);function f({children:e,initialComponents:n={}}){const o=t.useRef(new Map(Object.entries(n))),s={register:t.useCallback((e,n)=>{o.current.set(e,n)},[]),unregister:t.useCallback(e=>{o.current.delete(e)},[]),get:t.useCallback(e=>o.current.get(e),[]),has:t.useCallback(e=>o.current.has(e),[]),getRegisteredIds:t.useCallback(()=>Array.from(o.current.keys()),[])};return r.jsx(a.Provider,{value:s,children:e})}function m(){const e=t.useContext(a);if(!e)throw new Error("useComponentRegistry must be used within UIProvider");return e}const p=t.createContext(null);function h({children:e,renderers:n}){return r.jsx(p.Provider,{value:n,children:e})}function g(){const e=t.useContext(p);if(!e)throw new Error("useRenderers must be used within UIProvider");return e}function v({container:e,documentId:n}){const s=u(n),{provides:l}=o(),c=d(),i=g(),[a,f]=t.useState(null),m=(null==s?void 0:s.openMenus)||{},p=null==l?void 0:l.getSchema();t.useEffect(()=>{const e=Object.keys(m);if(e.length>0){const t=e[0];if(!t)return void f(null);const r=m[t];if(r&&r.triggeredByItemId){const e=c.getAnchor(n,r.triggeredByItemId);f({menuId:t,anchorEl:e})}else f(null)}else f(null)},[m,c,n]);if(!a||!p)return null;const h=p.menus[a.menuId];if(!h)return console.warn(`Menu "${a.menuId}" not found in schema`),null;const v=i.menu;return r.jsx(v,{schema:h,documentId:n,anchorEl:a.anchorEl,onClose:()=>{a&&(null==l||l.forDocument(n).closeMenu(a.menuId))},container:e})}function b({children:e,style:u,...c}){const{plugin:i}=s(),{provides:d}=o(),[a,f]=t.useState([]),[m,p]=t.useState([]),h=t.useRef(null),g=t.useRef(null),v=t.useRef(null),b=t.useRef(null),I=t.useMemo(()=>({containerRef:b,getContainer:()=>b.current}),[]),C=t.useCallback(e=>{const t=v.current;if(v.current=e,b.current=e,e&&e!==t&&i){const t=function(e){const n=e.getRootNode();return n instanceof ShadowRoot?n:document.head}(e);g.current=t;const r=t.querySelector(n.UI_SELECTORS.STYLES);if(r)return h.current=r,void(r.textContent=i.getStylesheet());const o=i.getStylesheet(),s=document.createElement("style");s.setAttribute(n.UI_ATTRIBUTES.STYLES,""),s.textContent=o,t instanceof ShadowRoot?t.insertBefore(s,t.firstChild):t.appendChild(s),h.current=s}},[i]);t.useEffect(()=>()=>{var e;(null==(e=h.current)?void 0:e.parentNode)&&!v.current&&h.current.remove(),h.current=null,g.current=null},[]),t.useEffect(()=>{if(i)return i.onStylesheetInvalidated(()=>{h.current&&(h.current.textContent=i.getStylesheet())})},[i]),t.useEffect(()=>{if(d)return f(d.getDisabledCategories()),p(d.getHiddenItems()),d.onCategoryChanged(({disabledCategories:e,hiddenItems:n})=>{f(e),p(n)})},[d]);const x=t.useMemo(()=>a.length>0?a.join(" "):void 0,[a]),S=t.useMemo(()=>m.length>0?m.join(" "):void 0,[m]),y=t.useMemo(()=>{const e={containerType:"inline-size"};return u&&"object"==typeof u?{...e,...u}:e},[u]),R={[n.UI_ATTRIBUTES.ROOT]:"",[n.UI_ATTRIBUTES.DISABLED_CATEGORIES]:x,[n.UI_ATTRIBUTES.HIDDEN_ITEMS]:S};return r.jsx(l.Provider,{value:I,children:r.jsx("div",{ref:C,...R,...c,style:y,children:e})})}exports.AnchorRegistryProvider=i,exports.ComponentRegistryProvider=f,exports.RenderersProvider=h,exports.UIContainerContext=l,exports.UIProvider=function({children:e,documentId:n,components:t={},renderers:o,menuContainer:s,...u}){return r.jsx(i,{children:r.jsx(f,{initialComponents:t,children:r.jsx(h,{renderers:o,children:r.jsxs(b,{...u,children:[e,r.jsx(v,{documentId:n,container:s})]})})})})},exports.useAnchorRegistry=d,exports.useComponentRegistry=m,exports.useItemRenderer=function(){const e=m();return{renderCustomComponent:(n,t,o)=>{const s=e.get(n);return s?r.jsx(s,{documentId:t,...o||{}}):(console.error(`Component "${n}" not found in registry`),null)}}},exports.useRegisterAnchor=function(e,n){const r=d(),o=t.useRef(null),s=t.useRef(e),u=t.useRef(n);return s.current=e,u.current=n,t.useCallback(e=>{const n=o.current;o.current=e,e?e!==n&&r.register(s.current,u.current,e):n&&r.unregister(s.current,u.current)},[r])},exports.useRenderers=g,exports.useSchemaRenderer=function(e){const n=g(),{provides:t}=o(),s=null==t?void 0:t.getSchema(),l=u(e);return{renderToolbar:(o,u)=>{if(!s||!t||!l)return null;const c=`${o}-${u}`,i=l.activeToolbars[c];if(!i)return null;const d=s.toolbars[i.toolbarId];if(!d)return console.warn(`Toolbar "${i.toolbarId}" not found in schema`),null;const a=!d.permanent?()=>{t.forDocument(e).closeToolbarSlot(o,u)}:void 0,f=n.toolbar;return r.jsx(f,{schema:d,documentId:e,isOpen:i.isOpen,onClose:a},i.toolbarId)},renderSidebar:(o,u)=>{var c;if(!s||!t||!l)return null;const i=`${o}-${u}`,d=l.activeSidebars[i];if(!d)return null;const a=null==(c=s.sidebars)?void 0:c[d.sidebarId];if(!a)return console.warn(`Sidebar "${d.sidebarId}" not found in schema`),null;const f=n.sidebar;return r.jsx(f,{schema:a,documentId:e,isOpen:d.isOpen,onClose:()=>{t.forDocument(e).closeSidebarSlot(o,u)}},d.sidebarId)},renderModal:()=>{var o;if(!s||!t||!(null==l?void 0:l.activeModal))return null;const{modalId:u,isOpen:c}=l.activeModal,i=null==(o=s.modals)?void 0:o[u];if(!i)return console.warn(`Modal "${u}" not found in schema`),null;const d=n.modal;return d?r.jsx(d,{schema:i,documentId:e,isOpen:c,onClose:()=>{t.forDocument(e).closeModal()},onExited:()=>{t.forDocument(e).clearModal()}},u):(console.warn("No modal renderer registered"),null)},getActiveToolbars:()=>l?Object.entries(l.activeToolbars).map(([e,n])=>{const[t,r]=e.split("-");return{placement:t,slot:r,toolbarId:n.toolbarId,isOpen:n.isOpen}}):[],getActiveSidebars:()=>l?Object.entries(l.activeSidebars).map(([e,n])=>{const[t,r]=e.split("-");return{placement:t,slot:r,sidebarId:n.sidebarId,isOpen:n.isOpen}}):[],renderOverlays:()=>{if(!(null==s?void 0:s.overlays)||!t)return null;const o=n.overlay;if(!o)return null;const u=Object.values(s.overlays);return 0===u.length?null:r.jsx(r.Fragment,{children:u.map(n=>r.jsx(o,{schema:n,documentId:e},n.id))})}}},exports.useSelectionMenu=function(e,n){var s;const{provides:u}=o(),l=g(),c=t.useCallback(t=>{var o;const s=null==u?void 0:u.getSchema(),c=null==(o=null==s?void 0:s.selectionMenus)?void 0:o[e];if(!c)return null;if(!t.selected)return null;const i=l.selectionMenu;return r.jsx(i,{schema:c,documentId:n,props:t})},[u,l,e,n]),i=null==u?void 0:u.getSchema();if(null==(s=null==i?void 0:i.selectionMenus)?void 0:s[e])return c},exports.useUICapability=o,exports.useUIContainer=function(){const e=t.useContext(l);if(!e)throw new Error("useUIContainer must be used within a UIProvider");return e},exports.useUIPlugin=s,exports.useUISchema=()=>{const{provides:e}=o();return(null==e?void 0:e.getSchema())??null},exports.useUIState=u,Object.keys(n).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>n[e]})});
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-ui.ts","../../src/shared/registries/anchor-registry.tsx","../../src/shared/registries/component-registry.tsx","../../src/shared/registries/renderers-registry.tsx","../../src/shared/auto-menu-renderer.tsx","../../src/shared/root.tsx","../../src/shared/provider.tsx","../../src/shared/hooks/use-item-renderer.tsx","../../src/shared/hooks/use-register-anchor.ts","../../src/shared/hooks/use-schema-renderer.tsx","../../src/shared/hooks/use-selection-menu.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { UIPlugin } from '@embedpdf/plugin-ui';\nimport { useState, useEffect } from '@framework';\nimport { UIDocumentState, UISchema } from '@embedpdf/plugin-ui';\n\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\n\n/**\n * Get UI state for a document\n */\nexport const useUIState = (documentId: string) => {\n const { provides } = useUICapability();\n const [state, setState] = useState<UIDocumentState | null>(null);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n setState(scope.getState());\n\n // Subscribe to changes\n const unsubToolbar = scope.onToolbarChanged(() => setState(scope.getState()));\n const unsubPanel = scope.onPanelChanged(() => setState(scope.getState()));\n const unsubModal = scope.onModalChanged(() => setState(scope.getState()));\n const unsubMenu = scope.onMenuChanged(() => setState(scope.getState()));\n\n return () => {\n unsubToolbar();\n unsubPanel();\n unsubModal();\n unsubMenu();\n };\n }, [provides, documentId]);\n\n return state;\n};\n\n/**\n * Get UI schema\n */\nexport const useUISchema = (): UISchema | null => {\n const { provides } = useUICapability();\n return provides?.getSchema() ?? null;\n};\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ReactNode } from '@framework';\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 AnchorRegistryContext = createContext<AnchorRegistry | null>(null);\n\nexport function AnchorRegistryProvider({ children }: { children: ReactNode }) {\n const anchorsRef = useRef<Map<string, HTMLElement>>(new Map());\n\n const registry: AnchorRegistry = {\n register: useCallback((documentId: string, itemId: string, element: HTMLElement) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.set(key, element);\n }, []),\n\n unregister: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.delete(key);\n }, []),\n\n getAnchor: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n return anchorsRef.current.get(key) || null;\n }, []),\n };\n\n return (\n <AnchorRegistryContext.Provider value={registry}>{children}</AnchorRegistryContext.Provider>\n );\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const context = useContext(AnchorRegistryContext);\n if (!context) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ComponentType, ReactNode } from '@framework';\nimport { 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: ComponentType<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): ComponentType<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst ComponentRegistryContext = createContext<ComponentRegistry | null>(null);\n\nexport interface ComponentRegistryProviderProps {\n children: ReactNode;\n initialComponents?: Record<string, ComponentType<BaseComponentProps>>;\n}\n\nexport function ComponentRegistryProvider({\n children,\n initialComponents = {},\n}: ComponentRegistryProviderProps) {\n const componentsRef = useRef<Map<string, ComponentType<BaseComponentProps>>>(\n new Map(Object.entries(initialComponents)),\n );\n\n const registry: ComponentRegistry = {\n register: useCallback((id: string, component: ComponentType<BaseComponentProps>) => {\n componentsRef.current.set(id, component);\n }, []),\n\n unregister: useCallback((id: string) => {\n componentsRef.current.delete(id);\n }, []),\n\n get: useCallback((id: string) => {\n return componentsRef.current.get(id);\n }, []),\n\n has: useCallback((id: string) => {\n return componentsRef.current.has(id);\n }, []),\n\n getRegisteredIds: useCallback(() => {\n return Array.from(componentsRef.current.keys());\n }, []),\n };\n\n return (\n <ComponentRegistryContext.Provider value={registry}>\n {children}\n </ComponentRegistryContext.Provider>\n );\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const context = useContext(ComponentRegistryContext);\n if (!context) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { createContext, useContext } from '@framework';\nimport type { ReactNode } from '@framework';\nimport { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RenderersContext = createContext<UIRenderers | null>(null);\n\nexport interface RenderersProviderProps {\n children: ReactNode;\n renderers: UIRenderers;\n}\n\nexport function RenderersProvider({ children, renderers }: RenderersProviderProps) {\n return <RenderersContext.Provider value={renderers}>{children}</RenderersContext.Provider>;\n}\n\nexport function useRenderers(): UIRenderers {\n const context = useContext(RenderersContext);\n if (!context) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return context;\n}\n","import { useState, useEffect } from '@framework';\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 */\nexport interface AutoMenuRendererProps {\n container?: HTMLElement | null;\n documentId: string; // Which document's menus to render\n}\n\nexport function AutoMenuRenderer({ container, documentId }: AutoMenuRendererProps) {\n const uiState = useUIState(documentId);\n const { provides } = useUICapability();\n const anchorRegistry = useAnchorRegistry();\n const renderers = useRenderers();\n\n const [activeMenu, setActiveMenu] = useState<{\n menuId: string;\n anchorEl: HTMLElement | null;\n } | null>(null);\n\n const openMenus = uiState?.openMenus || {};\n const schema = provides?.getSchema();\n\n // Update active menu when state changes\n useEffect(() => {\n const openMenuIds = Object.keys(openMenus);\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 setActiveMenu(null);\n return;\n }\n\n const menuState = openMenus[menuId];\n if (menuState && menuState.triggeredByItemId) {\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(documentId, menuState.triggeredByItemId);\n setActiveMenu({ menuId, anchorEl: anchor });\n } else {\n setActiveMenu(null);\n }\n } else {\n setActiveMenu(null);\n }\n }, [openMenus, anchorRegistry, documentId]);\n\n const handleClose = () => {\n if (activeMenu) {\n provides?.forDocument(documentId).closeMenu(activeMenu.menuId);\n }\n };\n\n if (!activeMenu || !schema) {\n return null;\n }\n\n const menuSchema = schema.menus[activeMenu.menuId];\n if (!menuSchema) {\n console.warn(`Menu \"${activeMenu.menuId}\" not found in schema`);\n return null;\n }\n\n // Use the user-provided menu renderer\n const MenuRenderer = renderers.menu;\n\n return (\n <MenuRenderer\n schema={menuSchema}\n documentId={documentId}\n anchorEl={activeMenu.anchorEl}\n onClose={handleClose}\n container={container}\n />\n );\n}\n","import { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\nimport { useUICapability, useUIPlugin } from './hooks/use-ui';\nimport {\n useState,\n useEffect,\n useRef,\n useMemo,\n useCallback,\n ReactNode,\n HTMLAttributes,\n} from '@framework';\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\ninterface UIRootProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n}\n\n/**\n * Internal component that handles:\n * 1. Injecting the generated stylesheet (into shadow root or document.head)\n * 2. Managing the data-disabled-categories attribute\n * 3. Updating styles on locale changes\n */\nexport function UIRoot({ children, ...restProps }: UIRootProps) {\n const { plugin } = useUIPlugin();\n const { provides } = useUICapability();\n const [disabledCategories, setDisabledCategories] = useState<string[]>([]);\n const styleElRef = useRef<HTMLStyleElement | null>(null);\n const styleTargetRef = useRef<HTMLElement | ShadowRoot | null>(null);\n const previousElementRef = useRef<HTMLDivElement | null>(null);\n\n // Callback ref that handles style injection when element mounts\n // Handles React Strict Mode by tracking previous element\n const rootRefCallback = useCallback(\n (element: HTMLDivElement | null) => {\n const previousElement = previousElementRef.current;\n\n // Update ref\n previousElementRef.current = element;\n\n // If element is null (unmount), don't do anything yet\n // React Strict Mode will remount, so we'll handle cleanup in useEffect\n if (!element) {\n return;\n }\n\n // If element changed (or is new) and plugin is available, inject styles\n if (element !== previousElement && plugin) {\n const styleTarget = getStyleTarget(element);\n styleTargetRef.current = styleTarget;\n\n // Check if styles already exist in this target\n const existingStyle = styleTarget.querySelector(\n UI_SELECTORS.STYLES,\n ) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleElRef.current = existingStyle;\n // Update content in case locale changed\n existingStyle.textContent = plugin.getStylesheet();\n return;\n }\n\n // Create and inject stylesheet\n const stylesheet = plugin.getStylesheet();\n const styleEl = document.createElement('style');\n styleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n styleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n // For shadow root, prepend before other content\n styleTarget.insertBefore(styleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(styleEl);\n }\n\n styleElRef.current = styleEl;\n }\n },\n [plugin],\n );\n\n // Cleanup on actual unmount (not Strict Mode remount)\n useEffect(() => {\n return () => {\n // Only cleanup if we're actually unmounting (not just Strict Mode)\n // The style element will be reused if component remounts\n if (styleElRef.current?.parentNode && !previousElementRef.current) {\n styleElRef.current.remove();\n }\n styleElRef.current = null;\n styleTargetRef.current = null;\n };\n }, []);\n\n // Subscribe to stylesheet invalidation (locale changes, schema merges)\n useEffect(() => {\n if (!plugin) return;\n\n return plugin.onStylesheetInvalidated(() => {\n // Update the style element content\n if (styleElRef.current) {\n styleElRef.current.textContent = plugin.getStylesheet();\n }\n });\n }, [plugin]);\n\n // Subscribe to category changes\n useEffect(() => {\n if (!provides) return;\n\n setDisabledCategories(provides.getDisabledCategories());\n\n return provides.onCategoryChanged(({ disabledCategories }) => {\n setDisabledCategories(disabledCategories);\n });\n }, [provides]);\n\n // Build the disabled categories attribute value\n const disabledCategoriesAttr = useMemo(\n () => (disabledCategories.length > 0 ? disabledCategories.join(' ') : undefined),\n [disabledCategories],\n );\n\n const rootProps = {\n [UI_ATTRIBUTES.ROOT]: '',\n [UI_ATTRIBUTES.DISABLED_CATEGORIES]: disabledCategoriesAttr,\n };\n\n return (\n <div\n ref={rootRefCallback}\n {...rootProps}\n {...restProps}\n style={{ containerType: 'inline-size', ...restProps.style }}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode, ComponentType, HTMLAttributes } from '@framework';\nimport { AnchorRegistryProvider } from './registries/anchor-registry';\nimport { ComponentRegistryProvider } from './registries/component-registry';\nimport { RenderersProvider } from './registries/renderers-registry';\nimport { BaseComponentProps, UIRenderers } from './types';\nimport { AutoMenuRenderer } from './auto-menu-renderer';\nimport { UIRoot } from './root';\n\n/**\n * UIProvider Props\n */\nexport interface UIProviderProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\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?: Record<string, ComponentType<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\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 * ```tsx\n * <EmbedPDF engine={engine} plugins={plugins}>\n * {({ pluginsReady }) => (\n * pluginsReady && (\n * <DocumentContext>\n * {({ activeDocumentId }) => (\n * 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 * <ViewerLayout />\n * </UIProvider>\n * )\n * )}\n * </DocumentContext>\n * )\n * )}\n * </EmbedPDF>\n * ```\n */\nexport function UIProvider({\n children,\n documentId,\n components = {},\n renderers,\n menuContainer,\n ...restProps\n}: UIProviderProps) {\n return (\n <AnchorRegistryProvider>\n <ComponentRegistryProvider initialComponents={components}>\n <RenderersProvider renderers={renderers}>\n <UIRoot {...restProps}>\n {children}\n {/* Automatically render menus for this document */}\n <AutoMenuRenderer documentId={documentId} container={menuContainer} />\n </UIRoot>\n </RenderersProvider>\n </ComponentRegistryProvider>\n </AnchorRegistryProvider>\n );\n}\n","import { 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 <Component documentId={documentId} {...(props || {})} />;\n },\n };\n}\n","import { useCallback, useRef } from '@framework';\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 callback to attach to the element\n *\n * @example\n * ```tsx\n * function ZoomButton({ documentId }: Props) {\n * const anchorRef = useRegisterAnchor(documentId, 'zoom-button');\n *\n * return <button ref={anchorRef}>Zoom</button>;\n * }\n * ```\n */\nexport function useRegisterAnchor(\n documentId: string,\n itemId: string,\n): (element: HTMLElement | null) => void {\n const registry = useAnchorRegistry();\n const elementRef = useRef<HTMLElement | null>(null);\n const documentIdRef = useRef(documentId);\n const itemIdRef = useRef(itemId);\n\n // Keep refs in sync\n documentIdRef.current = documentId;\n itemIdRef.current = itemId;\n\n // Return stable callback that uses refs\n return useCallback(\n (element: HTMLElement | null) => {\n // Store previous element\n const previousElement = elementRef.current;\n\n // Update ref\n elementRef.current = element;\n\n // Handle registration/unregistration\n if (element) {\n // Register new element\n if (element !== previousElement) {\n registry.register(documentIdRef.current, itemIdRef.current, element);\n }\n } else if (previousElement) {\n // Element is now null, but we had one before - unregister\n registry.unregister(documentIdRef.current, itemIdRef.current);\n }\n },\n [registry], // Only depend on registry!\n );\n}\n","import { useUICapability, useUIState } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * High-level hook for rendering UI from schema\n *\n * Provides simple functions to render toolbars and panels by placement+slot.\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 */\nexport function useSchemaRenderer(documentId: string) {\n const renderers = useRenderers();\n const { provides } = useUICapability();\n const schema = provides?.getSchema();\n const uiState = useUIState(documentId); // Subscribe to state changes\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 * ```tsx\n * {renderToolbar('top', 'main')}\n * {renderToolbar('top', 'secondary')}\n * ```\n */\n renderToolbar: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.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.forDocument(documentId).closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n const ToolbarRenderer = renderers.toolbar;\n\n // ALWAYS render, pass isOpen state\n return (\n <ToolbarRenderer\n key={toolbarSlot.toolbarId}\n schema={toolbarSchema}\n documentId={documentId}\n isOpen={toolbarSlot.isOpen}\n onClose={handleClose}\n />\n );\n },\n\n /**\n * Render a panel by placement and slot\n *\n * ALWAYS renders (when panel 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 * ```tsx\n * {renderPanel('left', 'main')}\n * {renderPanel('right', 'main')}\n * ```\n */\n renderPanel: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n const slotKey = `${placement}-${slot}`;\n const panelSlot = uiState.activePanels[slotKey];\n\n // If no panel in this slot, nothing to render\n if (!panelSlot) return null;\n\n const panelSchema = schema.panels[panelSlot.panelId];\n if (!panelSchema) {\n console.warn(`Panel \"${panelSlot.panelId}\" not found in schema`);\n return null;\n }\n\n const handleClose = () => {\n provides.forDocument(documentId).closePanelSlot(placement, slot);\n };\n\n const PanelRenderer = renderers.panel;\n\n // ALWAYS render, pass isOpen state\n // Your renderer decides whether to return null or animate\n return (\n <PanelRenderer\n key={panelSlot.panelId}\n schema={panelSchema}\n documentId={documentId}\n isOpen={panelSlot.isOpen}\n onClose={handleClose}\n />\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) return [];\n return Object.entries(uiState.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 panels for this document\n * Useful for batch rendering or debugging\n */\n getActivePanels: () => {\n if (!uiState) return [];\n return Object.entries(uiState.activePanels).map(([slotKey, panelSlot]) => {\n const [placement, slot] = slotKey.split('-');\n return {\n placement,\n slot,\n panelId: panelSlot.panelId,\n isOpen: panelSlot.isOpen,\n };\n });\n },\n };\n}\n","import { useCallback } from '@framework';\nimport { SelectionMenuPropsBase, SelectionMenuRenderFn } from '@embedpdf/utils/@framework';\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\n * @returns A render function compatible with layer selectionMenu props\n */\nexport function useSelectionMenu<TContext extends { type: string }>(\n menuId: string,\n documentId: string,\n): SelectionMenuRenderFn<TContext> | undefined {\n const { provides } = useUICapability();\n const renderers = useRenderers();\n\n const renderFn = useCallback(\n (props: SelectionMenuPropsBase<TContext>) => {\n const schema = provides?.getSchema();\n const menuSchema = schema?.selectionMenus?.[menuId];\n\n if (!menuSchema) {\n return null;\n }\n\n if (!props.selected) {\n return null;\n }\n\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n return <SelectionMenuRenderer schema={menuSchema} documentId={documentId} props={props} />;\n },\n [provides, renderers, menuId, documentId],\n );\n\n // Return undefined if schema doesn't have this menu\n const schema = provides?.getSchema();\n if (!schema?.selectionMenus?.[menuId]) {\n return undefined;\n }\n\n return renderFn;\n}\n"],"names":["useUICapability","useCapability","UIPlugin","id","useUIPlugin","usePlugin","useUIState","documentId","provides","state","setState","useState","useEffect","scope","forDocument","getState","unsubToolbar","onToolbarChanged","unsubPanel","onPanelChanged","unsubModal","onModalChanged","unsubMenu","onMenuChanged","AnchorRegistryContext","createContext","AnchorRegistryProvider","children","anchorsRef","useRef","Map","registry","register","useCallback","itemId","element","key","current","set","unregister","delete","getAnchor","get","Provider","value","useAnchorRegistry","context","useContext","Error","ComponentRegistryContext","ComponentRegistryProvider","initialComponents","componentsRef","Object","entries","component","has","getRegisteredIds","Array","from","keys","useComponentRegistry","RenderersContext","RenderersProvider","renderers","useRenderers","AutoMenuRenderer","container","uiState","anchorRegistry","activeMenu","setActiveMenu","openMenus","schema","getSchema","openMenuIds","length","menuId","menuState","triggeredByItemId","anchor","anchorEl","menuSchema","menus","console","warn","MenuRenderer","menu","jsx","onClose","closeMenu","UIRoot","restProps","plugin","disabledCategories","setDisabledCategories","styleElRef","styleTargetRef","previousElementRef","rootRefCallback","previousElement","styleTarget","root","getRootNode","ShadowRoot","document","head","getStyleTarget","existingStyle","querySelector","UI_SELECTORS","STYLES","textContent","getStylesheet","stylesheet","styleEl","createElement","setAttribute","UI_ATTRIBUTES","insertBefore","firstChild","appendChild","_a","parentNode","remove","onStylesheetInvalidated","getDisabledCategories","onCategoryChanged","disabledCategoriesAttr","useMemo","join","rootProps","ROOT","DISABLED_CATEGORIES","ref","style","containerType","components","menuContainer","jsxs","componentRegistry","renderCustomComponent","componentId","props","Component","error","elementRef","documentIdRef","itemIdRef","renderToolbar","placement","slot","slotKey","toolbarSlot","activeToolbars","toolbarSchema","toolbars","toolbarId","handleClose","permanent","closeToolbarSlot","ToolbarRenderer","toolbar","isOpen","renderPanel","panelSlot","activePanels","panelSchema","panels","panelId","PanelRenderer","panel","closePanelSlot","getActiveToolbars","map","split","getActivePanels","renderFn","selectionMenus","selected","SelectionMenuRenderer","selectionMenu"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-ui.ts","../../src/shared/hooks/use-ui-container.ts","../../src/shared/registries/anchor-registry.tsx","../../src/shared/registries/component-registry.tsx","../../src/shared/registries/renderers-registry.tsx","../../src/shared/auto-menu-renderer.tsx","../../src/shared/root.tsx","../../src/shared/provider.tsx","../../src/shared/hooks/use-item-renderer.tsx","../../src/shared/hooks/use-register-anchor.ts","../../src/shared/hooks/use-schema-renderer.tsx","../../src/shared/hooks/use-selection-menu.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { UIPlugin } from '@embedpdf/plugin-ui';\nimport { useState, useEffect } from '@framework';\nimport { UIDocumentState, UISchema } from '@embedpdf/plugin-ui';\n\nexport const useUICapability = () => useCapability<UIPlugin>(UIPlugin.id);\nexport const useUIPlugin = () => usePlugin<UIPlugin>(UIPlugin.id);\n\n/**\n * Get UI state for a document\n */\nexport const useUIState = (documentId: string) => {\n const { provides } = useUICapability();\n const [state, setState] = useState<UIDocumentState | null>(null);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n setState(scope.getState());\n\n // Subscribe to changes\n const unsubToolbar = scope.onToolbarChanged(() => setState(scope.getState()));\n const unsubSidebar = scope.onSidebarChanged(() => setState(scope.getState()));\n const unsubModal = scope.onModalChanged(() => setState(scope.getState()));\n const unsubMenu = scope.onMenuChanged(() => setState(scope.getState()));\n\n return () => {\n unsubToolbar();\n unsubSidebar();\n unsubModal();\n unsubMenu();\n };\n }, [provides, documentId]);\n\n return state;\n};\n\n/**\n * Get UI schema\n */\nexport const useUISchema = (): UISchema | null => {\n const { provides } = useUICapability();\n return provides?.getSchema() ?? null;\n};\n","import { createContext, useContext, RefObject } from '@framework';\n\nexport interface UIContainerContextValue {\n /** Reference to the UIRoot container element */\n containerRef: RefObject<HTMLDivElement>;\n /** Get the container element (may be null if not mounted) */\n getContainer: () => HTMLDivElement | null;\n}\n\nexport const UIContainerContext = createContext<UIContainerContextValue | null>(null);\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 * ```tsx\n * function MyComponent() {\n * const { containerRef, getContainer } = useUIContainer();\n *\n * // Use containerRef for ResizeObserver\n * useEffect(() => {\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 * return () => observer.disconnect();\n * }, [getContainer]);\n *\n * // Or portal to the container\n * return createPortal(<Modal />, getContainer()!);\n * }\n * ```\n */\nexport function useUIContainer(): UIContainerContextValue {\n const context = useContext(UIContainerContext);\n if (!context) {\n throw new Error('useUIContainer must be used within a UIProvider');\n }\n return context;\n}\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ReactNode } from '@framework';\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 AnchorRegistryContext = createContext<AnchorRegistry | null>(null);\n\nexport function AnchorRegistryProvider({ children }: { children: ReactNode }) {\n const anchorsRef = useRef<Map<string, HTMLElement>>(new Map());\n\n const registry: AnchorRegistry = {\n register: useCallback((documentId: string, itemId: string, element: HTMLElement) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.set(key, element);\n }, []),\n\n unregister: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n anchorsRef.current.delete(key);\n }, []),\n\n getAnchor: useCallback((documentId: string, itemId: string) => {\n const key = `${documentId}:${itemId}`;\n return anchorsRef.current.get(key) || null;\n }, []),\n };\n\n return (\n <AnchorRegistryContext.Provider value={registry}>{children}</AnchorRegistryContext.Provider>\n );\n}\n\nexport function useAnchorRegistry(): AnchorRegistry {\n const context = useContext(AnchorRegistryContext);\n if (!context) {\n throw new Error('useAnchorRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { createContext, useContext, useRef, useCallback } from '@framework';\nimport type { ComponentType, ReactNode } from '@framework';\nimport { 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: ComponentType<BaseComponentProps>): void;\n unregister(id: string): void;\n get(id: string): ComponentType<BaseComponentProps> | undefined;\n has(id: string): boolean;\n getRegisteredIds(): string[];\n}\n\nconst ComponentRegistryContext = createContext<ComponentRegistry | null>(null);\n\nexport interface ComponentRegistryProviderProps {\n children: ReactNode;\n initialComponents?: Record<string, ComponentType<BaseComponentProps>>;\n}\n\nexport function ComponentRegistryProvider({\n children,\n initialComponents = {},\n}: ComponentRegistryProviderProps) {\n const componentsRef = useRef<Map<string, ComponentType<BaseComponentProps>>>(\n new Map(Object.entries(initialComponents)),\n );\n\n const registry: ComponentRegistry = {\n register: useCallback((id: string, component: ComponentType<BaseComponentProps>) => {\n componentsRef.current.set(id, component);\n }, []),\n\n unregister: useCallback((id: string) => {\n componentsRef.current.delete(id);\n }, []),\n\n get: useCallback((id: string) => {\n return componentsRef.current.get(id);\n }, []),\n\n has: useCallback((id: string) => {\n return componentsRef.current.has(id);\n }, []),\n\n getRegisteredIds: useCallback(() => {\n return Array.from(componentsRef.current.keys());\n }, []),\n };\n\n return (\n <ComponentRegistryContext.Provider value={registry}>\n {children}\n </ComponentRegistryContext.Provider>\n );\n}\n\nexport function useComponentRegistry(): ComponentRegistry {\n const context = useContext(ComponentRegistryContext);\n if (!context) {\n throw new Error('useComponentRegistry must be used within UIProvider');\n }\n return context;\n}\n","import { createContext, useContext } from '@framework';\nimport type { ReactNode } from '@framework';\nimport { UIRenderers } from '../types';\n\n/**\n * Renderers Registry\n *\n * Provides access to user-supplied renderers (toolbar, panel, menu).\n */\nconst RenderersContext = createContext<UIRenderers | null>(null);\n\nexport interface RenderersProviderProps {\n children: ReactNode;\n renderers: UIRenderers;\n}\n\nexport function RenderersProvider({ children, renderers }: RenderersProviderProps) {\n return <RenderersContext.Provider value={renderers}>{children}</RenderersContext.Provider>;\n}\n\nexport function useRenderers(): UIRenderers {\n const context = useContext(RenderersContext);\n if (!context) {\n throw new Error('useRenderers must be used within UIProvider');\n }\n return context;\n}\n","import { useState, useEffect } from '@framework';\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 */\nexport interface AutoMenuRendererProps {\n container?: HTMLElement | null;\n documentId: string; // Which document's menus to render\n}\n\nexport function AutoMenuRenderer({ container, documentId }: AutoMenuRendererProps) {\n const uiState = useUIState(documentId);\n const { provides } = useUICapability();\n const anchorRegistry = useAnchorRegistry();\n const renderers = useRenderers();\n\n const [activeMenu, setActiveMenu] = useState<{\n menuId: string;\n anchorEl: HTMLElement | null;\n } | null>(null);\n\n const openMenus = uiState?.openMenus || {};\n const schema = provides?.getSchema();\n\n // Update active menu when state changes\n useEffect(() => {\n const openMenuIds = Object.keys(openMenus);\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 setActiveMenu(null);\n return;\n }\n\n const menuState = openMenus[menuId];\n if (menuState && menuState.triggeredByItemId) {\n // Look up anchor with documentId scope\n const anchor = anchorRegistry.getAnchor(documentId, menuState.triggeredByItemId);\n setActiveMenu({ menuId, anchorEl: anchor });\n } else {\n setActiveMenu(null);\n }\n } else {\n setActiveMenu(null);\n }\n }, [openMenus, anchorRegistry, documentId]);\n\n const handleClose = () => {\n if (activeMenu) {\n provides?.forDocument(documentId).closeMenu(activeMenu.menuId);\n }\n };\n\n if (!activeMenu || !schema) {\n return null;\n }\n\n const menuSchema = schema.menus[activeMenu.menuId];\n if (!menuSchema) {\n console.warn(`Menu \"${activeMenu.menuId}\" not found in schema`);\n return null;\n }\n\n // Use the user-provided menu renderer\n const MenuRenderer = renderers.menu;\n\n return (\n <MenuRenderer\n schema={menuSchema}\n documentId={documentId}\n anchorEl={activeMenu.anchorEl}\n onClose={handleClose}\n container={container}\n />\n );\n}\n","import { UI_ATTRIBUTES, UI_SELECTORS } from '@embedpdf/plugin-ui';\nimport { useUICapability, useUIPlugin } from './hooks/use-ui';\nimport { UIContainerContext, UIContainerContextValue } from './hooks/use-ui-container';\nimport {\n useState,\n useEffect,\n useRef,\n useMemo,\n useCallback,\n ReactNode,\n HTMLAttributes,\n} from '@framework';\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\ninterface UIRootProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n}\n\n/**\n * Internal component that handles:\n * 1. Injecting the generated stylesheet (into shadow root or document.head)\n * 2. Managing the data-disabled-categories attribute\n * 3. Updating styles on locale changes\n */\nexport function UIRoot({ children, style, ...restProps }: UIRootProps) {\n const { plugin } = useUIPlugin();\n const { provides } = useUICapability();\n const [disabledCategories, setDisabledCategories] = useState<string[]>([]);\n const [hiddenItems, setHiddenItems] = useState<string[]>([]);\n const styleElRef = useRef<HTMLStyleElement | null>(null);\n const styleTargetRef = useRef<HTMLElement | ShadowRoot | null>(null);\n const previousElementRef = useRef<HTMLDivElement | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Create container context value (memoized to prevent unnecessary re-renders)\n const containerContextValue = useMemo<UIContainerContextValue>(\n () => ({\n containerRef,\n getContainer: () => containerRef.current,\n }),\n [],\n );\n\n // Callback ref that handles style injection when element mounts\n // Handles React Strict Mode by tracking previous element\n const rootRefCallback = useCallback(\n (element: HTMLDivElement | null) => {\n const previousElement = previousElementRef.current;\n\n // Update refs\n previousElementRef.current = element;\n (containerRef as any).current = element;\n\n // If element is null (unmount), don't do anything yet\n // React Strict Mode will remount, so we'll handle cleanup in useEffect\n if (!element) {\n return;\n }\n\n // If element changed (or is new) and plugin is available, inject styles\n if (element !== previousElement && plugin) {\n const styleTarget = getStyleTarget(element);\n styleTargetRef.current = styleTarget;\n\n // Check if styles already exist in this target\n const existingStyle = styleTarget.querySelector(\n UI_SELECTORS.STYLES,\n ) as HTMLStyleElement | null;\n\n if (existingStyle) {\n styleElRef.current = existingStyle;\n // Update content in case locale changed\n existingStyle.textContent = plugin.getStylesheet();\n return;\n }\n\n // Create and inject stylesheet\n const stylesheet = plugin.getStylesheet();\n const styleEl = document.createElement('style');\n styleEl.setAttribute(UI_ATTRIBUTES.STYLES, '');\n styleEl.textContent = stylesheet;\n\n if (styleTarget instanceof ShadowRoot) {\n // For shadow root, prepend before other content\n styleTarget.insertBefore(styleEl, styleTarget.firstChild);\n } else {\n styleTarget.appendChild(styleEl);\n }\n\n styleElRef.current = styleEl;\n }\n },\n [plugin],\n );\n\n // Cleanup on actual unmount (not Strict Mode remount)\n useEffect(() => {\n return () => {\n // Only cleanup if we're actually unmounting (not just Strict Mode)\n // The style element will be reused if component remounts\n if (styleElRef.current?.parentNode && !previousElementRef.current) {\n styleElRef.current.remove();\n }\n styleElRef.current = null;\n styleTargetRef.current = null;\n };\n }, []);\n\n // Subscribe to stylesheet invalidation (locale changes, schema merges)\n useEffect(() => {\n if (!plugin) return;\n\n return plugin.onStylesheetInvalidated(() => {\n // Update the style element content\n if (styleElRef.current) {\n styleElRef.current.textContent = plugin.getStylesheet();\n }\n });\n }, [plugin]);\n\n // Subscribe to category and hidden items changes\n useEffect(() => {\n if (!provides) return;\n\n setDisabledCategories(provides.getDisabledCategories());\n setHiddenItems(provides.getHiddenItems());\n\n return provides.onCategoryChanged(({ disabledCategories, hiddenItems }) => {\n setDisabledCategories(disabledCategories);\n setHiddenItems(hiddenItems);\n });\n }, [provides]);\n\n // Build the disabled categories attribute value\n const disabledCategoriesAttr = useMemo(\n () => (disabledCategories.length > 0 ? disabledCategories.join(' ') : undefined),\n [disabledCategories],\n );\n\n // Build the hidden items attribute value\n const hiddenItemsAttr = useMemo(\n () => (hiddenItems.length > 0 ? hiddenItems.join(' ') : undefined),\n [hiddenItems],\n );\n\n const combinedStyle = useMemo(() => {\n const base = { containerType: 'inline-size' as const };\n if (style && typeof style === 'object') {\n return { ...base, ...style };\n }\n return base;\n }, [style]);\n\n const rootProps = {\n [UI_ATTRIBUTES.ROOT]: '',\n [UI_ATTRIBUTES.DISABLED_CATEGORIES]: disabledCategoriesAttr,\n [UI_ATTRIBUTES.HIDDEN_ITEMS]: hiddenItemsAttr,\n };\n\n return (\n <UIContainerContext.Provider value={containerContextValue}>\n <div ref={rootRefCallback} {...rootProps} {...restProps} style={combinedStyle}>\n {children}\n </div>\n </UIContainerContext.Provider>\n );\n}\n","import type { ReactNode, ComponentType, HTMLAttributes } from '@framework';\nimport { AnchorRegistryProvider } from './registries/anchor-registry';\nimport { ComponentRegistryProvider } from './registries/component-registry';\nimport { RenderersProvider } from './registries/renderers-registry';\nimport { BaseComponentProps, UIRenderers } from './types';\nimport { AutoMenuRenderer } from './auto-menu-renderer';\nimport { UIRoot } from './root';\n\n/**\n * UIProvider Props\n */\nexport interface UIProviderProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\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?: Record<string, ComponentType<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\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 * ```tsx\n * <EmbedPDF engine={engine} plugins={plugins}>\n * {({ pluginsReady }) => (\n * pluginsReady && (\n * <DocumentContext>\n * {({ activeDocumentId }) => (\n * 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 * <ViewerLayout />\n * </UIProvider>\n * )\n * )}\n * </DocumentContext>\n * )\n * )}\n * </EmbedPDF>\n * ```\n */\nexport function UIProvider({\n children,\n documentId,\n components = {},\n renderers,\n menuContainer,\n ...restProps\n}: UIProviderProps) {\n return (\n <AnchorRegistryProvider>\n <ComponentRegistryProvider initialComponents={components}>\n <RenderersProvider renderers={renderers}>\n <UIRoot {...restProps}>\n {children}\n {/* Automatically render menus for this document */}\n <AutoMenuRenderer documentId={documentId} container={menuContainer} />\n </UIRoot>\n </RenderersProvider>\n </ComponentRegistryProvider>\n </AnchorRegistryProvider>\n );\n}\n","import { 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 <Component documentId={documentId} {...(props || {})} />;\n },\n };\n}\n","import { useCallback, useRef } from '@framework';\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 callback to attach to the element\n *\n * @example\n * ```tsx\n * function ZoomButton({ documentId }: Props) {\n * const anchorRef = useRegisterAnchor(documentId, 'zoom-button');\n *\n * return <button ref={anchorRef}>Zoom</button>;\n * }\n * ```\n */\nexport function useRegisterAnchor(\n documentId: string,\n itemId: string,\n): (element: HTMLElement | null) => void {\n const registry = useAnchorRegistry();\n const elementRef = useRef<HTMLElement | null>(null);\n const documentIdRef = useRef(documentId);\n const itemIdRef = useRef(itemId);\n\n // Keep refs in sync\n documentIdRef.current = documentId;\n itemIdRef.current = itemId;\n\n // Return stable callback that uses refs\n return useCallback(\n (element: HTMLElement | null) => {\n // Store previous element\n const previousElement = elementRef.current;\n\n // Update ref\n elementRef.current = element;\n\n // Handle registration/unregistration\n if (element) {\n // Register new element\n if (element !== previousElement) {\n registry.register(documentIdRef.current, itemIdRef.current, element);\n }\n } else if (previousElement) {\n // Element is now null, but we had one before - unregister\n registry.unregister(documentIdRef.current, itemIdRef.current);\n }\n },\n [registry], // Only depend on registry!\n );\n}\n","import { useUICapability, useUIState } from './use-ui';\nimport { useRenderers } from '../registries/renderers-registry';\n\n/**\n * High-level hook 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 */\nexport function useSchemaRenderer(documentId: string) {\n const renderers = useRenderers();\n const { provides } = useUICapability();\n const schema = provides?.getSchema();\n const uiState = useUIState(documentId); // Subscribe to state changes\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 * ```tsx\n * {renderToolbar('top', 'main')}\n * {renderToolbar('top', 'secondary')}\n * ```\n */\n renderToolbar: (placement: 'top' | 'bottom' | 'left' | 'right', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n\n const slotKey = `${placement}-${slot}`;\n const toolbarSlot = uiState.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.forDocument(documentId).closeToolbarSlot(placement, slot);\n }\n : undefined;\n\n const ToolbarRenderer = renderers.toolbar;\n\n // ALWAYS render, pass isOpen state\n return (\n <ToolbarRenderer\n key={toolbarSlot.toolbarId}\n schema={toolbarSchema}\n documentId={documentId}\n isOpen={toolbarSlot.isOpen}\n onClose={handleClose}\n />\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 * ```tsx\n * {renderSidebar('left', 'main')}\n * {renderSidebar('right', 'main')}\n * ```\n */\n renderSidebar: (placement: 'left' | 'right' | 'top' | 'bottom', slot: string) => {\n if (!schema || !provides || !uiState) return null;\n const slotKey = `${placement}-${slot}`;\n const sidebarSlot = uiState.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.forDocument(documentId).closeSidebarSlot(placement, slot);\n };\n\n const SidebarRenderer = renderers.sidebar;\n\n // ALWAYS render, pass isOpen state\n // Your renderer decides whether to return null or animate\n return (\n <SidebarRenderer\n key={sidebarSlot.sidebarId}\n schema={sidebarSchema}\n documentId={documentId}\n isOpen={sidebarSlot.isOpen}\n onClose={handleClose}\n />\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 * ```tsx\n * {renderModal()}\n * ```\n */\n renderModal: () => {\n if (!schema || !provides || !uiState?.activeModal) return null;\n\n const { modalId, isOpen } = uiState.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.forDocument(documentId).closeModal();\n };\n\n const handleExited = () => {\n provides.forDocument(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 (\n <ModalRenderer\n key={modalId}\n schema={modalSchema}\n documentId={documentId}\n isOpen={isOpen}\n onClose={handleClose}\n onExited={handleExited}\n />\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) return [];\n return Object.entries(uiState.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) return [];\n return Object.entries(uiState.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 * ```tsx\n * <div className=\"relative\">\n * {children}\n * {renderOverlays()}\n * </div>\n * ```\n */\n renderOverlays: () => {\n if (!schema?.overlays || !provides) 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 (\n <>\n {overlays.map((overlaySchema) => (\n <OverlayRenderer\n key={overlaySchema.id}\n schema={overlaySchema}\n documentId={documentId}\n />\n ))}\n </>\n );\n },\n };\n}\n","import { useCallback } from '@framework';\nimport { SelectionMenuPropsBase, SelectionMenuRenderFn } from '@embedpdf/utils/@framework';\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\n * @returns A render function compatible with layer selectionMenu props\n */\nexport function useSelectionMenu<TContext extends { type: string }>(\n menuId: string,\n documentId: string,\n): SelectionMenuRenderFn<TContext> | undefined {\n const { provides } = useUICapability();\n const renderers = useRenderers();\n\n const renderFn = useCallback(\n (props: SelectionMenuPropsBase<TContext>) => {\n const schema = provides?.getSchema();\n const menuSchema = schema?.selectionMenus?.[menuId];\n\n if (!menuSchema) {\n return null;\n }\n\n if (!props.selected) {\n return null;\n }\n\n const SelectionMenuRenderer = renderers.selectionMenu;\n\n return <SelectionMenuRenderer schema={menuSchema} documentId={documentId} props={props} />;\n },\n [provides, renderers, menuId, documentId],\n );\n\n // Return undefined if schema doesn't have this menu\n const schema = provides?.getSchema();\n if (!schema?.selectionMenus?.[menuId]) {\n return undefined;\n }\n\n return renderFn;\n}\n"],"names":["useUICapability","useCapability","UIPlugin","id","useUIPlugin","usePlugin","useUIState","documentId","provides","state","setState","useState","useEffect","scope","forDocument","getState","unsubToolbar","onToolbarChanged","unsubSidebar","onSidebarChanged","unsubModal","onModalChanged","unsubMenu","onMenuChanged","UIContainerContext","createContext","AnchorRegistryContext","AnchorRegistryProvider","children","anchorsRef","useRef","Map","registry","register","useCallback","itemId","element","key","current","set","unregister","delete","getAnchor","get","Provider","value","useAnchorRegistry","context","useContext","Error","ComponentRegistryContext","ComponentRegistryProvider","initialComponents","componentsRef","Object","entries","component","has","getRegisteredIds","Array","from","keys","useComponentRegistry","RenderersContext","RenderersProvider","renderers","useRenderers","AutoMenuRenderer","container","uiState","anchorRegistry","activeMenu","setActiveMenu","openMenus","schema","getSchema","openMenuIds","length","menuId","menuState","triggeredByItemId","anchor","anchorEl","menuSchema","menus","console","warn","MenuRenderer","menu","jsx","onClose","closeMenu","UIRoot","style","restProps","plugin","disabledCategories","setDisabledCategories","hiddenItems","setHiddenItems","styleElRef","styleTargetRef","previousElementRef","containerRef","containerContextValue","useMemo","getContainer","rootRefCallback","previousElement","styleTarget","root","getRootNode","ShadowRoot","document","head","getStyleTarget","existingStyle","querySelector","UI_SELECTORS","STYLES","textContent","getStylesheet","stylesheet","styleEl","createElement","setAttribute","UI_ATTRIBUTES","insertBefore","firstChild","appendChild","_a","parentNode","remove","onStylesheetInvalidated","getDisabledCategories","getHiddenItems","onCategoryChanged","disabledCategoriesAttr","join","hiddenItemsAttr","combinedStyle","base","containerType","rootProps","ROOT","DISABLED_CATEGORIES","HIDDEN_ITEMS","ref","components","menuContainer","jsxs","componentRegistry","renderCustomComponent","componentId","props","Component","error","elementRef","documentIdRef","itemIdRef","renderToolbar","placement","slot","slotKey","toolbarSlot","activeToolbars","toolbarSchema","toolbars","toolbarId","handleClose","permanent","closeToolbarSlot","ToolbarRenderer","toolbar","isOpen","renderSidebar","sidebarSlot","activeSidebars","sidebarSchema","sidebars","sidebarId","SidebarRenderer","sidebar","closeSidebarSlot","renderModal","activeModal","modalId","modalSchema","modals","ModalRenderer","modal","closeModal","onExited","clearModal","getActiveToolbars","map","split","getActiveSidebars","renderOverlays","overlays","OverlayRenderer","overlay","values","Fragment","overlaySchema","renderFn","selectionMenus","selected","SelectionMenuRenderer","selectionMenu"],"mappings":"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"}
|