@flanksource/clicky-ui 0.3.43 → 0.3.45
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/README.md +2 -0
- package/dist/ai.cjs +4 -2
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.js +3 -1
- package/dist/components/MdxEditorField.cjs +40 -22
- package/dist/components/MdxEditorField.cjs.map +1 -1
- package/dist/components/MdxEditorField.d.ts.map +1 -1
- package/dist/components/MdxEditorField.js +41 -23
- package/dist/components/MdxEditorField.js.map +1 -1
- package/dist/components/json-schema-form-item-summary.cjs +6 -1
- package/dist/components/json-schema-form-item-summary.cjs.map +1 -1
- package/dist/components/json-schema-form-item-summary.d.ts.map +1 -1
- package/dist/components/json-schema-form-item-summary.js +6 -1
- package/dist/components/json-schema-form-item-summary.js.map +1 -1
- package/dist/data/CodeBlock.cjs +3 -2
- package/dist/data/CodeBlock.cjs.map +1 -1
- package/dist/data/CodeBlock.d.ts +3 -1
- package/dist/data/CodeBlock.d.ts.map +1 -1
- package/dist/data/CodeBlock.js +3 -2
- package/dist/data/CodeBlock.js.map +1 -1
- package/dist/data/CodeLine.cjs +22 -0
- package/dist/data/CodeLine.cjs.map +1 -0
- package/dist/data/CodeLine.d.ts +8 -0
- package/dist/data/CodeLine.d.ts.map +1 -0
- package/dist/data/CodeLine.js +22 -0
- package/dist/data/CodeLine.js.map +1 -0
- package/dist/data/GraphDiagram.cjs +18 -6
- package/dist/data/GraphDiagram.cjs.map +1 -1
- package/dist/data/GraphDiagram.d.ts +9 -1
- package/dist/data/GraphDiagram.d.ts.map +1 -1
- package/dist/data/GraphDiagram.js +18 -6
- package/dist/data/GraphDiagram.js.map +1 -1
- package/dist/data/GraphDiagramNodes.cjs +38 -29
- package/dist/data/GraphDiagramNodes.cjs.map +1 -1
- package/dist/data/GraphDiagramNodes.d.ts +8 -2
- package/dist/data/GraphDiagramNodes.d.ts.map +1 -1
- package/dist/data/GraphDiagramNodes.js +38 -29
- package/dist/data/GraphDiagramNodes.js.map +1 -1
- package/dist/data/JsonView.cjs +118 -49
- package/dist/data/JsonView.cjs.map +1 -1
- package/dist/data/JsonView.d.ts +13 -10
- package/dist/data/JsonView.d.ts.map +1 -1
- package/dist/data/JsonView.js +118 -49
- package/dist/data/JsonView.js.map +1 -1
- package/dist/data/MarkdownEditor.cjs +2 -2
- package/dist/data/MarkdownEditor.cjs.map +1 -1
- package/dist/data/MarkdownEditor.js +2 -2
- package/dist/data/MarkdownEditor.js.map +1 -1
- package/dist/data/ai/AdvancedChatConfig.cjs +90 -249
- package/dist/data/ai/AdvancedChatConfig.cjs.map +1 -1
- package/dist/data/ai/AdvancedChatConfig.d.ts +2 -5
- package/dist/data/ai/AdvancedChatConfig.d.ts.map +1 -1
- package/dist/data/ai/AdvancedChatConfig.js +91 -250
- package/dist/data/ai/AdvancedChatConfig.js.map +1 -1
- package/dist/data/ai/ChatButton.cjs +2 -0
- package/dist/data/ai/ChatButton.cjs.map +1 -1
- package/dist/data/ai/ChatButton.d.ts +1 -1
- package/dist/data/ai/ChatButton.d.ts.map +1 -1
- package/dist/data/ai/ChatButton.js +2 -0
- package/dist/data/ai/ChatButton.js.map +1 -1
- package/dist/data/ai/ChatFab.cjs +5 -1
- package/dist/data/ai/ChatFab.cjs.map +1 -1
- package/dist/data/ai/ChatFab.d.ts +5 -1
- package/dist/data/ai/ChatFab.d.ts.map +1 -1
- package/dist/data/ai/ChatFab.js +5 -1
- package/dist/data/ai/ChatFab.js.map +1 -1
- package/dist/data/ai/ChatWindow.cjs +2 -8
- package/dist/data/ai/ChatWindow.cjs.map +1 -1
- package/dist/data/ai/ChatWindow.d.ts.map +1 -1
- package/dist/data/ai/ChatWindow.js +2 -8
- package/dist/data/ai/ChatWindow.js.map +1 -1
- package/dist/data/ai/ChatWindow.runtime.cjs +0 -5
- package/dist/data/ai/ChatWindow.runtime.cjs.map +1 -1
- package/dist/data/ai/ChatWindow.runtime.d.ts +0 -2
- package/dist/data/ai/ChatWindow.runtime.d.ts.map +1 -1
- package/dist/data/ai/ChatWindow.runtime.js +0 -5
- package/dist/data/ai/ChatWindow.runtime.js.map +1 -1
- package/dist/data/ai/PromptRunEditor/runtimeActions.cjs +2 -2
- package/dist/data/ai/PromptRunEditor/runtimeActions.cjs.map +1 -1
- package/dist/data/ai/PromptRunEditor/runtimeActions.d.ts.map +1 -1
- package/dist/data/ai/PromptRunEditor/runtimeActions.js +2 -2
- package/dist/data/ai/PromptRunEditor/runtimeActions.js.map +1 -1
- package/dist/data/ai/SessionInspector.approvals.cjs +3 -6
- package/dist/data/ai/SessionInspector.approvals.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.approvals.js +3 -6
- package/dist/data/ai/SessionInspector.approvals.js.map +1 -1
- package/dist/data/ai/SessionViewer.cjs +13 -4
- package/dist/data/ai/SessionViewer.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.d.ts +5 -1
- package/dist/data/ai/SessionViewer.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.header.cjs +1 -1
- package/dist/data/ai/SessionViewer.header.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.header.js +1 -1
- package/dist/data/ai/SessionViewer.header.js.map +1 -1
- package/dist/data/ai/SessionViewer.js +13 -4
- package/dist/data/ai/SessionViewer.js.map +1 -1
- package/dist/data/ai/SessionViewer.model.cjs +2 -0
- package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.model.d.ts +1 -0
- package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.model.js +2 -0
- package/dist/data/ai/SessionViewer.model.js.map +1 -1
- package/dist/data/ai/SessionViewer.rows.cjs +41 -27
- package/dist/data/ai/SessionViewer.rows.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.rows.d.ts +4 -2
- package/dist/data/ai/SessionViewer.rows.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.rows.js +41 -27
- package/dist/data/ai/SessionViewer.rows.js.map +1 -1
- package/dist/data/ai/SessionViewer.tokens.cjs +35 -0
- package/dist/data/ai/SessionViewer.tokens.cjs.map +1 -0
- package/dist/data/ai/SessionViewer.tokens.d.ts +5 -0
- package/dist/data/ai/SessionViewer.tokens.d.ts.map +1 -0
- package/dist/data/ai/SessionViewer.tokens.js +35 -0
- package/dist/data/ai/SessionViewer.tokens.js.map +1 -0
- package/dist/data/ai/SessionViewer.tool-cost.cjs +128 -0
- package/dist/data/ai/SessionViewer.tool-cost.cjs.map +1 -0
- package/dist/data/ai/SessionViewer.tool-cost.d.ts +6 -0
- package/dist/data/ai/SessionViewer.tool-cost.d.ts.map +1 -0
- package/dist/data/ai/SessionViewer.tool-cost.js +128 -0
- package/dist/data/ai/SessionViewer.tool-cost.js.map +1 -0
- package/dist/data/ai/SessionViewer.tool-details.cjs +3 -1
- package/dist/data/ai/SessionViewer.tool-details.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.tool-details.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.tool-details.js +3 -1
- package/dist/data/ai/SessionViewer.tool-details.js.map +1 -1
- package/dist/data/ai/SessionViewer.tool-row.cjs +55 -35
- package/dist/data/ai/SessionViewer.tool-row.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.tool-row.d.ts +2 -1
- package/dist/data/ai/SessionViewer.tool-row.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.tool-row.js +55 -35
- package/dist/data/ai/SessionViewer.tool-row.js.map +1 -1
- package/dist/data/ai/SessionViewer.unified.d.ts +4 -0
- package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
- package/dist/data/ai/SessionViewerMenu.cjs +18 -1
- package/dist/data/ai/SessionViewerMenu.cjs.map +1 -1
- package/dist/data/ai/SessionViewerMenu.d.ts +6 -1
- package/dist/data/ai/SessionViewerMenu.d.ts.map +1 -1
- package/dist/data/ai/SessionViewerMenu.js +18 -1
- package/dist/data/ai/SessionViewerMenu.js.map +1 -1
- package/dist/data/ai/ToolPreferences.cjs +149 -143
- package/dist/data/ai/ToolPreferences.cjs.map +1 -1
- package/dist/data/ai/ToolPreferences.d.ts +20 -6
- package/dist/data/ai/ToolPreferences.d.ts.map +1 -1
- package/dist/data/ai/ToolPreferences.js +151 -146
- package/dist/data/ai/ToolPreferences.js.map +1 -1
- package/dist/data/ai/ToolPreferences.model.cjs +14 -10
- package/dist/data/ai/ToolPreferences.model.cjs.map +1 -1
- package/dist/data/ai/ToolPreferences.model.d.ts +1 -0
- package/dist/data/ai/ToolPreferences.model.d.ts.map +1 -1
- package/dist/data/ai/ToolPreferences.model.js +15 -11
- package/dist/data/ai/ToolPreferences.model.js.map +1 -1
- package/dist/data/ai/ToolPreferencesList.cjs +1 -0
- package/dist/data/ai/ToolPreferencesList.cjs.map +1 -1
- package/dist/data/ai/ToolPreferencesList.d.ts +4 -1
- package/dist/data/ai/ToolPreferencesList.d.ts.map +1 -1
- package/dist/data/ai/ToolPreferencesList.js +2 -1
- package/dist/data/ai/ToolPreferencesList.js.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.cjs +52 -53
- package/dist/data/ai/ToolSchemaBrowser.cjs.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.d.ts +6 -3
- package/dist/data/ai/ToolSchemaBrowser.d.ts.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.directory-header.cjs +9 -13
- package/dist/data/ai/ToolSchemaBrowser.directory-header.cjs.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.directory-header.d.ts +5 -2
- package/dist/data/ai/ToolSchemaBrowser.directory-header.d.ts.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.directory-header.js +9 -13
- package/dist/data/ai/ToolSchemaBrowser.directory-header.js.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.js +53 -54
- package/dist/data/ai/ToolSchemaBrowser.js.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.model.cjs +19 -27
- package/dist/data/ai/ToolSchemaBrowser.model.cjs.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.model.d.ts +9 -6
- package/dist/data/ai/ToolSchemaBrowser.model.d.ts.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.model.js +20 -28
- package/dist/data/ai/ToolSchemaBrowser.model.js.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.sidebar.cjs +12 -213
- package/dist/data/ai/ToolSchemaBrowser.sidebar.cjs.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.sidebar.d.ts +1 -3
- package/dist/data/ai/ToolSchemaBrowser.sidebar.d.ts.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.sidebar.js +13 -214
- package/dist/data/ai/ToolSchemaBrowser.sidebar.js.map +1 -1
- package/dist/data/ai/ToolSchemaBrowser.strategies.cjs +54 -0
- package/dist/data/ai/ToolSchemaBrowser.strategies.cjs.map +1 -0
- package/dist/data/ai/ToolSchemaBrowser.strategies.d.ts +9 -0
- package/dist/data/ai/ToolSchemaBrowser.strategies.d.ts.map +1 -0
- package/dist/data/ai/ToolSchemaBrowser.strategies.js +54 -0
- package/dist/data/ai/ToolSchemaBrowser.strategies.js.map +1 -0
- package/dist/data/ai/ToolSchemaBrowser.tree.cjs +179 -0
- package/dist/data/ai/ToolSchemaBrowser.tree.cjs.map +1 -0
- package/dist/data/ai/ToolSchemaBrowser.tree.d.ts +20 -0
- package/dist/data/ai/ToolSchemaBrowser.tree.d.ts.map +1 -0
- package/dist/data/ai/ToolSchemaBrowser.tree.js +179 -0
- package/dist/data/ai/ToolSchemaBrowser.tree.js.map +1 -0
- package/dist/data/ai/index.d.ts +2 -2
- package/dist/data/ai/index.d.ts.map +1 -1
- package/dist/data/ai/runtime-profiles/PermissionStrategiesEditor.cjs +8 -28
- package/dist/data/ai/runtime-profiles/PermissionStrategiesEditor.cjs.map +1 -1
- package/dist/data/ai/runtime-profiles/PermissionStrategiesEditor.d.ts.map +1 -1
- package/dist/data/ai/runtime-profiles/PermissionStrategiesEditor.js +9 -29
- package/dist/data/ai/runtime-profiles/PermissionStrategiesEditor.js.map +1 -1
- package/dist/data/ai/session-token-sizing.d.ts +32 -0
- package/dist/data/ai/session-token-sizing.d.ts.map +1 -0
- package/dist/data/ai/use-session-token-sizing.cjs +73 -0
- package/dist/data/ai/use-session-token-sizing.cjs.map +1 -0
- package/dist/data/ai/use-session-token-sizing.d.ts +21 -0
- package/dist/data/ai/use-session-token-sizing.d.ts.map +1 -0
- package/dist/data/ai/use-session-token-sizing.js +73 -0
- package/dist/data/ai/use-session-token-sizing.js.map +1 -0
- package/dist/data/call-graph/CallGraph.cjs +250 -0
- package/dist/data/call-graph/CallGraph.cjs.map +1 -0
- package/dist/data/call-graph/CallGraph.d.ts +69 -0
- package/dist/data/call-graph/CallGraph.d.ts.map +1 -0
- package/dist/data/call-graph/CallGraph.js +250 -0
- package/dist/data/call-graph/CallGraph.js.map +1 -0
- package/dist/data/call-graph/CallGraphGlyphs.cjs +39 -0
- package/dist/data/call-graph/CallGraphGlyphs.cjs.map +1 -0
- package/dist/data/call-graph/CallGraphGlyphs.d.ts +19 -0
- package/dist/data/call-graph/CallGraphGlyphs.d.ts.map +1 -0
- package/dist/data/call-graph/CallGraphGlyphs.js +39 -0
- package/dist/data/call-graph/CallGraphGlyphs.js.map +1 -0
- package/dist/data/call-graph/CallGraphMembers.cjs +50 -0
- package/dist/data/call-graph/CallGraphMembers.cjs.map +1 -0
- package/dist/data/call-graph/CallGraphMembers.d.ts +22 -0
- package/dist/data/call-graph/CallGraphMembers.d.ts.map +1 -0
- package/dist/data/call-graph/CallGraphMembers.js +50 -0
- package/dist/data/call-graph/CallGraphMembers.js.map +1 -0
- package/dist/data/call-graph/CallGraphParts.cjs +204 -0
- package/dist/data/call-graph/CallGraphParts.cjs.map +1 -0
- package/dist/data/call-graph/CallGraphParts.d.ts +44 -0
- package/dist/data/call-graph/CallGraphParts.d.ts.map +1 -0
- package/dist/data/call-graph/CallGraphParts.js +204 -0
- package/dist/data/call-graph/CallGraphParts.js.map +1 -0
- package/dist/data/call-graph/CallGraphToolbar.cjs +186 -0
- package/dist/data/call-graph/CallGraphToolbar.cjs.map +1 -0
- package/dist/data/call-graph/CallGraphToolbar.d.ts +45 -0
- package/dist/data/call-graph/CallGraphToolbar.d.ts.map +1 -0
- package/dist/data/call-graph/CallGraphToolbar.js +186 -0
- package/dist/data/call-graph/CallGraphToolbar.js.map +1 -0
- package/dist/data/call-graph/call-graph-exclude.cjs +75 -0
- package/dist/data/call-graph/call-graph-exclude.cjs.map +1 -0
- package/dist/data/call-graph/call-graph-exclude.d.ts +31 -0
- package/dist/data/call-graph/call-graph-exclude.d.ts.map +1 -0
- package/dist/data/call-graph/call-graph-exclude.js +75 -0
- package/dist/data/call-graph/call-graph-exclude.js.map +1 -0
- package/dist/data/call-graph/call-graph-labels.cjs +96 -0
- package/dist/data/call-graph/call-graph-labels.cjs.map +1 -0
- package/dist/data/call-graph/call-graph-labels.d.ts +53 -0
- package/dist/data/call-graph/call-graph-labels.d.ts.map +1 -0
- package/dist/data/call-graph/call-graph-labels.js +96 -0
- package/dist/data/call-graph/call-graph-labels.js.map +1 -0
- package/dist/data/call-graph/call-graph-model.cjs +161 -0
- package/dist/data/call-graph/call-graph-model.cjs.map +1 -0
- package/dist/data/call-graph/call-graph-model.d.ts +102 -0
- package/dist/data/call-graph/call-graph-model.d.ts.map +1 -0
- package/dist/data/call-graph/call-graph-model.js +161 -0
- package/dist/data/call-graph/call-graph-model.js.map +1 -0
- package/dist/data/call-graph/call-graph-vocabulary.cjs +177 -0
- package/dist/data/call-graph/call-graph-vocabulary.cjs.map +1 -0
- package/dist/data/call-graph/call-graph-vocabulary.d.ts +72 -0
- package/dist/data/call-graph/call-graph-vocabulary.d.ts.map +1 -0
- package/dist/data/call-graph/call-graph-vocabulary.js +177 -0
- package/dist/data/call-graph/call-graph-vocabulary.js.map +1 -0
- package/dist/data/call-graph/call-graph.fixtures.d.ts +63 -0
- package/dist/data/call-graph/call-graph.fixtures.d.ts.map +1 -0
- package/dist/data/call-graph/index.d.ts +7 -0
- package/dist/data/call-graph/index.d.ts.map +1 -0
- package/dist/data/call-graph/types.cjs +5 -0
- package/dist/data/call-graph/types.cjs.map +1 -0
- package/dist/data/call-graph/types.d.ts +135 -0
- package/dist/data/call-graph/types.d.ts.map +1 -0
- package/dist/data/call-graph/types.js +5 -0
- package/dist/data/call-graph/types.js.map +1 -0
- package/dist/data/call-graph/use-call-graph.cjs +93 -0
- package/dist/data/call-graph/use-call-graph.cjs.map +1 -0
- package/dist/data/call-graph/use-call-graph.d.ts +24 -0
- package/dist/data/call-graph/use-call-graph.d.ts.map +1 -0
- package/dist/data/call-graph/use-call-graph.js +93 -0
- package/dist/data/call-graph/use-call-graph.js.map +1 -0
- package/dist/data/chat/Chat.cjs +39 -15
- package/dist/data/chat/Chat.cjs.map +1 -1
- package/dist/data/chat/Chat.d.ts.map +1 -1
- package/dist/data/chat/Chat.fixtures.cjs +2 -2
- package/dist/data/chat/Chat.fixtures.cjs.map +1 -1
- package/dist/data/chat/Chat.fixtures.js +2 -2
- package/dist/data/chat/Chat.fixtures.js.map +1 -1
- package/dist/data/chat/Chat.js +40 -16
- package/dist/data/chat/Chat.js.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.cjs +3 -6
- package/dist/data/chat/ChatRuntimeToolbar.cjs.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.d.ts.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.js +3 -6
- package/dist/data/chat/ChatRuntimeToolbar.js.map +1 -1
- package/dist/data/chat/ContextMeter.bar.cjs +5 -8
- package/dist/data/chat/ContextMeter.bar.cjs.map +1 -1
- package/dist/data/chat/ContextMeter.bar.d.ts +1 -1
- package/dist/data/chat/ContextMeter.bar.d.ts.map +1 -1
- package/dist/data/chat/ContextMeter.bar.js +5 -8
- package/dist/data/chat/ContextMeter.bar.js.map +1 -1
- package/dist/data/chat/ContextMeter.cjs +18 -19
- package/dist/data/chat/ContextMeter.cjs.map +1 -1
- package/dist/data/chat/ContextMeter.d.ts +1 -1
- package/dist/data/chat/ContextMeter.d.ts.map +1 -1
- package/dist/data/chat/ContextMeter.js +18 -19
- package/dist/data/chat/ContextMeter.js.map +1 -1
- package/dist/data/chat/Conversation.cjs +2 -1
- package/dist/data/chat/Conversation.cjs.map +1 -1
- package/dist/data/chat/Conversation.d.ts +4 -1
- package/dist/data/chat/Conversation.d.ts.map +1 -1
- package/dist/data/chat/Conversation.js +2 -1
- package/dist/data/chat/Conversation.js.map +1 -1
- package/dist/data/chat/Message.cjs +39 -12
- package/dist/data/chat/Message.cjs.map +1 -1
- package/dist/data/chat/Message.d.ts +1 -1
- package/dist/data/chat/Message.d.ts.map +1 -1
- package/dist/data/chat/Message.js +40 -13
- package/dist/data/chat/Message.js.map +1 -1
- package/dist/data/chat/ToolCall.cjs +60 -5
- package/dist/data/chat/ToolCall.cjs.map +1 -1
- package/dist/data/chat/ToolCall.d.ts +7 -2
- package/dist/data/chat/ToolCall.d.ts.map +1 -1
- package/dist/data/chat/ToolCall.js +60 -5
- package/dist/data/chat/ToolCall.js.map +1 -1
- package/dist/data/chat/approval.cjs.map +1 -1
- package/dist/data/chat/approval.d.ts +2 -2
- package/dist/data/chat/approval.d.ts.map +1 -1
- package/dist/data/chat/approval.js.map +1 -1
- package/dist/data/chat/tool-policy.cjs +59 -2
- package/dist/data/chat/tool-policy.cjs.map +1 -1
- package/dist/data/chat/tool-policy.d.ts +9 -0
- package/dist/data/chat/tool-policy.d.ts.map +1 -1
- package/dist/data/chat/tool-policy.js +59 -2
- package/dist/data/chat/tool-policy.js.map +1 -1
- package/dist/data/chat/types.cjs +6 -0
- package/dist/data/chat/types.cjs.map +1 -1
- package/dist/data/chat/types.d.ts +13 -4
- package/dist/data/chat/types.d.ts.map +1 -1
- package/dist/data/chat/types.js +7 -1
- package/dist/data/chat/types.js.map +1 -1
- package/dist/data/chat/usage-snapshot.cjs +11 -6
- package/dist/data/chat/usage-snapshot.cjs.map +1 -1
- package/dist/data/chat/usage-snapshot.d.ts +0 -1
- package/dist/data/chat/usage-snapshot.d.ts.map +1 -1
- package/dist/data/chat/usage-snapshot.js +11 -6
- package/dist/data/chat/usage-snapshot.js.map +1 -1
- package/dist/data/diagnostics/RenderedStackTrace.cjs +2 -1
- package/dist/data/diagnostics/RenderedStackTrace.cjs.map +1 -1
- package/dist/data/diagnostics/RenderedStackTrace.d.ts +3 -1
- package/dist/data/diagnostics/RenderedStackTrace.d.ts.map +1 -1
- package/dist/data/diagnostics/RenderedStackTrace.js +2 -1
- package/dist/data/diagnostics/RenderedStackTrace.js.map +1 -1
- package/dist/data/diagnostics/StackFrameRow.cjs +3 -3
- package/dist/data/diagnostics/StackFrameRow.cjs.map +1 -1
- package/dist/data/diagnostics/StackFrameRow.js +3 -3
- package/dist/data/diagnostics/StackFrameRow.js.map +1 -1
- package/dist/data/graph-columns-layout.cjs +26 -10
- package/dist/data/graph-columns-layout.cjs.map +1 -1
- package/dist/data/graph-columns-layout.d.ts +8 -0
- package/dist/data/graph-columns-layout.d.ts.map +1 -1
- package/dist/data/graph-columns-layout.js +26 -10
- package/dist/data/graph-columns-layout.js.map +1 -1
- package/dist/data/graph-diagram-model.cjs.map +1 -1
- package/dist/data/graph-diagram-model.d.ts +5 -0
- package/dist/data/graph-diagram-model.d.ts.map +1 -1
- package/dist/data/graph-diagram-model.js.map +1 -1
- package/dist/data/graph-edge-routing.cjs +9 -3
- package/dist/data/graph-edge-routing.cjs.map +1 -1
- package/dist/data/graph-edge-routing.d.ts +2 -0
- package/dist/data/graph-edge-routing.d.ts.map +1 -1
- package/dist/data/graph-edge-routing.js +9 -3
- package/dist/data/graph-edge-routing.js.map +1 -1
- package/dist/data/json-view/JsonSource.cjs +84 -0
- package/dist/data/json-view/JsonSource.cjs.map +1 -0
- package/dist/data/json-view/JsonSource.d.ts +7 -0
- package/dist/data/json-view/JsonSource.d.ts.map +1 -0
- package/dist/data/json-view/JsonSource.js +84 -0
- package/dist/data/json-view/JsonSource.js.map +1 -0
- package/dist/data/json-view/parse-prefix.cjs +132 -0
- package/dist/data/json-view/parse-prefix.cjs.map +1 -0
- package/dist/data/json-view/parse-prefix.d.ts +11 -0
- package/dist/data/json-view/parse-prefix.d.ts.map +1 -0
- package/dist/data/json-view/parse-prefix.js +132 -0
- package/dist/data/json-view/parse-prefix.js.map +1 -0
- package/dist/data/json-view/parse-source.cjs +62 -0
- package/dist/data/json-view/parse-source.cjs.map +1 -0
- package/dist/data/json-view/parse-source.d.ts +16 -0
- package/dist/data/json-view/parse-source.d.ts.map +1 -0
- package/dist/data/json-view/parse-source.js +62 -0
- package/dist/data/json-view/parse-source.js.map +1 -0
- package/dist/data/json-view/story-fixtures.d.ts +6 -0
- package/dist/data/json-view/story-fixtures.d.ts.map +1 -0
- package/dist/data/json-view/types.d.ts +14 -0
- package/dist/data/json-view/types.d.ts.map +1 -0
- package/dist/data/runtime/RuntimeBar.cjs +2 -5
- package/dist/data/runtime/RuntimeBar.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBar.d.ts +2 -4
- package/dist/data/runtime/RuntimeBar.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBar.fields.cjs +2 -2
- package/dist/data/runtime/RuntimeBar.fields.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBar.fields.js +2 -2
- package/dist/data/runtime/RuntimeBar.fields.js.map +1 -1
- package/dist/data/runtime/RuntimeBar.js +2 -5
- package/dist/data/runtime/RuntimeBar.js.map +1 -1
- package/dist/data/runtime/RuntimeBar.selection.cjs +4 -1
- package/dist/data/runtime/RuntimeBar.selection.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBar.selection.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBar.selection.js +5 -2
- package/dist/data/runtime/RuntimeBar.selection.js.map +1 -1
- package/dist/data/runtime/RuntimeBarActions.cjs +1 -5
- package/dist/data/runtime/RuntimeBarActions.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBarActions.d.ts +1 -1
- package/dist/data/runtime/RuntimeBarActions.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBarActions.js +1 -5
- package/dist/data/runtime/RuntimeBarActions.js.map +1 -1
- package/dist/data/runtime/RuntimeBarIdentity.cjs +6 -14
- package/dist/data/runtime/RuntimeBarIdentity.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBarIdentity.d.ts +0 -1
- package/dist/data/runtime/RuntimeBarIdentity.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBarIdentity.js +6 -14
- package/dist/data/runtime/RuntimeBarIdentity.js.map +1 -1
- package/dist/data/runtime/RuntimeBarLayout.cjs +8 -27
- package/dist/data/runtime/RuntimeBarLayout.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBarLayout.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBarLayout.js +8 -27
- package/dist/data/runtime/RuntimeBarLayout.js.map +1 -1
- package/dist/data/runtime/RuntimeBarSegment.cjs +1 -5
- package/dist/data/runtime/RuntimeBarSegment.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBarSegment.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBarSegment.js +1 -5
- package/dist/data/runtime/RuntimeBarSegment.js.map +1 -1
- package/dist/data/runtime/use-runtime-bar-overflow.cjs +4 -10
- package/dist/data/runtime/use-runtime-bar-overflow.cjs.map +1 -1
- package/dist/data/runtime/use-runtime-bar-overflow.d.ts +1 -2
- package/dist/data/runtime/use-runtime-bar-overflow.d.ts.map +1 -1
- package/dist/data/runtime/use-runtime-bar-overflow.js +4 -10
- package/dist/data/runtime/use-runtime-bar-overflow.js.map +1 -1
- package/dist/data/test-runner/TestRunner.fixtures.d.ts +0 -20
- package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunner.large-fixtures.d.ts +22 -0
- package/dist/data/test-runner/TestRunner.large-fixtures.d.ts.map +1 -0
- package/dist/data/use-pan-zoom.cjs +23 -10
- package/dist/data/use-pan-zoom.cjs.map +1 -1
- package/dist/data/use-pan-zoom.d.ts +16 -2
- package/dist/data/use-pan-zoom.d.ts.map +1 -1
- package/dist/data/use-pan-zoom.js +23 -10
- package/dist/data/use-pan-zoom.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +28 -2
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +2 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +26 -0
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +30 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/Workspace.cjs +13 -4
- package/dist/layout/Workspace.cjs.map +1 -1
- package/dist/layout/Workspace.d.ts +7 -2
- package/dist/layout/Workspace.d.ts.map +1 -1
- package/dist/layout/Workspace.js +13 -4
- package/dist/layout/Workspace.js.map +1 -1
- package/dist/lib/permission-strategies.cjs +71 -11
- package/dist/lib/permission-strategies.cjs.map +1 -1
- package/dist/lib/permission-strategies.d.ts +3 -0
- package/dist/lib/permission-strategies.d.ts.map +1 -1
- package/dist/lib/permission-strategies.js +71 -11
- package/dist/lib/permission-strategies.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +5 -3
- package/dist/data/ai/ToolSchemaBrowser.selection-toolbar.cjs +0 -57
- package/dist/data/ai/ToolSchemaBrowser.selection-toolbar.cjs.map +0 -1
- package/dist/data/ai/ToolSchemaBrowser.selection-toolbar.d.ts +0 -7
- package/dist/data/ai/ToolSchemaBrowser.selection-toolbar.d.ts.map +0 -1
- package/dist/data/ai/ToolSchemaBrowser.selection-toolbar.js +0 -57
- package/dist/data/ai/ToolSchemaBrowser.selection-toolbar.js.map +0 -1
- package/dist/data/runtime/RuntimeBar.context.cjs +0 -8
- package/dist/data/runtime/RuntimeBar.context.cjs.map +0 -1
- package/dist/data/runtime/RuntimeBar.context.d.ts +0 -2
- package/dist/data/runtime/RuntimeBar.context.d.ts.map +0 -1
- package/dist/data/runtime/RuntimeBar.context.js +0 -8
- package/dist/data/runtime/RuntimeBar.context.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuntimeBarSegment.js","sources":["../../../src/data/runtime/RuntimeBarSegment.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"RuntimeBarSegment.js","sources":["../../../src/data/runtime/RuntimeBarSegment.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { UiCheck, UiChevronDown } from \"../../icons\";\nimport { cn } from \"../../lib/utils\";\nimport {\n DropdownMenu,\n type DropdownMenuItem,\n} from \"../../overlay/DropdownMenu\";\nimport { Icon } from \"../Icon\";\n\nexport const SEGMENT_CAPTION_CLASS =\n \"truncate text-xs font-semibold text-foreground\";\nexport const SEGMENT_KEY_CLASS =\n \"text-[11px] font-semibold uppercase leading-none tracking-wide text-muted-foreground\";\n\n// One trigger in the segmented runtime bar: a dropdown whose button carries the\n// segment's current value. `min-w-0` on the root lets a segment shrink past its\n// content — without it the flex item's automatic minimum size is its max-content\n// width, so a row of segments overflows a narrow container instead of ellipsing\n// its captions.\nexport function RuntimeSegment({\n items,\n menuLabel,\n title,\n header,\n disabled = false,\n hideChevron = false,\n className,\n children,\n}: {\n items: DropdownMenuItem[];\n menuLabel: string;\n title: string;\n header?: ReactNode;\n disabled?: boolean;\n /** Drops the trailing chevron, for a segment whose caption is a bare glyph. */\n hideChevron?: boolean;\n className?: string | undefined;\n children: ReactNode;\n}) {\n return (\n <DropdownMenu\n align=\"left\"\n menuLabel={menuLabel}\n items={items}\n {...(header ? { header } : {})}\n menuClassName=\"max-h-[70vh] min-w-56 max-w-80 overflow-y-auto\"\n className={cn(\n \"h-control-h min-w-0 border-l border-border first:border-l-0 [&>span]:min-w-0 [&>span]:w-full\",\n className,\n )}\n trigger={\n <button\n type=\"button\"\n disabled={disabled}\n title={title}\n className=\"inline-flex h-full w-full min-w-0 items-center gap-1.5 px-density-2 transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring [[aria-expanded=true]_&]:bg-muted\"\n >\n <RuntimeSegmentCaption hideChevron={hideChevron}>\n {children}\n </RuntimeSegmentCaption>\n </button>\n }\n />\n );\n}\n\nexport function RuntimeSegmentCaption({\n children,\n hideChevron = false,\n}: {\n children: ReactNode;\n hideChevron?: boolean;\n}) {\n return (\n <>\n {children}\n {!hideChevron && (\n <Icon\n icon={UiChevronDown}\n className=\"size-3 shrink-0 text-muted-foreground/70\"\n />\n )}\n </>\n );\n}\n\nexport function SegmentItemLabel({\n text,\n hint,\n selected,\n stacked = false,\n}: {\n text: string;\n hint?: string | undefined;\n selected: boolean;\n stacked?: boolean | undefined;\n}) {\n return (\n <>\n <span className=\"min-w-0 flex-1\">\n <span className={cn(\"block truncate\", selected && \"font-semibold\")}>\n {text}\n </span>\n {hint && stacked && (\n <span className=\"block whitespace-normal text-[11px] leading-4 text-muted-foreground\">\n {hint}\n </span>\n )}\n </span>\n {hint && !stacked && (\n <span className=\"shrink-0 text-[11px] text-muted-foreground\">\n {hint}\n </span>\n )}\n <Icon\n icon={UiCheck}\n className={cn(\n \"size-3.5 shrink-0 text-primary\",\n !selected && \"invisible\",\n )}\n />\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;AASO,MAAM,wBACX;AACK,MAAM,oBACX;AAOK,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAUG;AACD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN;AAAA,MACA;AAAA,MACC,GAAI,SAAS,EAAE,OAAA,IAAW,CAAA;AAAA,MAC3B,eAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,SACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,WAAU;AAAA,UAEV,UAAA,oBAAC,uBAAA,EAAsB,aACpB,SAAA,CACH;AAAA,QAAA;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AAIR;AAEO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA,cAAc;AAChB,GAGG;AACD,SACE,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA;AAAA,IACA,CAAC,eACA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EACZ,GAEJ;AAEJ;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAKG;AACD,SACE,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,qBAAC,QAAA,EAAK,WAAU,kBACd,UAAA;AAAA,MAAA,oBAAC,UAAK,WAAW,GAAG,kBAAkB,YAAY,eAAe,GAC9D,UAAA,MACH;AAAA,MACC,QAAQ,WACP,oBAAC,QAAA,EAAK,WAAU,uEACb,UAAA,KAAA,CACH;AAAA,IAAA,GAEJ;AAAA,IACC,QAAQ,CAAC,+BACP,QAAA,EAAK,WAAU,8CACb,UAAA,MACH;AAAA,IAEF;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AAAA,UACT;AAAA,UACA,CAAC,YAAY;AAAA,QAAA;AAAA,MACf;AAAA,IAAA;AAAA,EACF,GACF;AAEJ;"}
|
|
@@ -5,13 +5,12 @@ function runtimeBarInlineCount({
|
|
|
5
5
|
width,
|
|
6
6
|
identity,
|
|
7
7
|
trigger,
|
|
8
|
-
fields
|
|
9
|
-
gap
|
|
8
|
+
fields
|
|
10
9
|
}) {
|
|
11
|
-
let occupied = identity + trigger
|
|
10
|
+
let occupied = identity + trigger;
|
|
12
11
|
let count = 0;
|
|
13
12
|
for (const field of fields) {
|
|
14
|
-
occupied += field
|
|
13
|
+
occupied += field;
|
|
15
14
|
if (occupied > width) break;
|
|
16
15
|
count++;
|
|
17
16
|
}
|
|
@@ -31,16 +30,11 @@ function useRuntimeBarOverflow() {
|
|
|
31
30
|
throw new Error(
|
|
32
31
|
"Runtime bar measurement requires identity and a menu trigger"
|
|
33
32
|
);
|
|
34
|
-
const spacing = getComputedStyle(measurement).columnGap;
|
|
35
|
-
const gap = spacing === "normal" || spacing === "" ? 0 : Number.parseFloat(spacing);
|
|
36
|
-
if (!Number.isFinite(gap))
|
|
37
|
-
throw new Error(`Invalid runtime bar spacing: ${spacing}`);
|
|
38
33
|
const count = runtimeBarInlineCount({
|
|
39
34
|
width: width - 2,
|
|
40
35
|
identity: identity.getBoundingClientRect().width,
|
|
41
36
|
trigger: trigger.getBoundingClientRect().width,
|
|
42
|
-
fields: controls.map((node) => node.getBoundingClientRect().width)
|
|
43
|
-
gap
|
|
37
|
+
fields: controls.map((node) => node.getBoundingClientRect().width)
|
|
44
38
|
});
|
|
45
39
|
setVisibleCount((previous) => previous === count ? previous : count);
|
|
46
40
|
}, [container, measurement]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-runtime-bar-overflow.cjs","sources":["../../../src/data/runtime/use-runtime-bar-overflow.ts"],"sourcesContent":["import { useCallback, useLayoutEffect, useState } from \"react\";\n\nexport function runtimeBarInlineCount({\n width,\n identity,\n trigger,\n fields,\n
|
|
1
|
+
{"version":3,"file":"use-runtime-bar-overflow.cjs","sources":["../../../src/data/runtime/use-runtime-bar-overflow.ts"],"sourcesContent":["import { useCallback, useLayoutEffect, useState } from \"react\";\n\nexport function runtimeBarInlineCount({\n width,\n identity,\n trigger,\n fields,\n}: {\n width: number;\n identity: number;\n trigger: number;\n fields: number[];\n}) {\n let occupied = identity + trigger;\n let count = 0;\n for (const field of fields) {\n occupied += field;\n if (occupied > width) break;\n count++;\n }\n return count;\n}\n\nexport function useRuntimeBarOverflow() {\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [measurement, setMeasurement] = useState<HTMLDivElement | null>(null);\n const [visibleCount, setVisibleCount] = useState(Infinity);\n\n const measure = useCallback(() => {\n if (!container || !measurement) return;\n const width = container.getBoundingClientRect().width;\n if (width <= 0) return;\n const [identity, ...controls] = [...measurement.children];\n const trigger = controls.pop();\n if (!identity || !trigger)\n throw new Error(\n \"Runtime bar measurement requires identity and a menu trigger\",\n );\n const count = runtimeBarInlineCount({\n width: width - 2,\n identity: identity.getBoundingClientRect().width,\n trigger: trigger.getBoundingClientRect().width,\n fields: controls.map((node) => node.getBoundingClientRect().width),\n });\n setVisibleCount((previous) => (previous === count ? previous : count));\n }, [container, measurement]);\n\n useLayoutEffect(() => {\n measure();\n });\n\n useLayoutEffect(() => {\n if (!container || !measurement) return;\n measure();\n const observer =\n typeof ResizeObserver === \"undefined\"\n ? undefined\n : new ResizeObserver(measure);\n observer?.observe(container);\n observer?.observe(measurement);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, [container, measurement, measure]);\n\n return {\n containerRef: setContainer,\n measurementRef: setMeasurement,\n visibleCount,\n };\n}\n"],"names":["useState","useCallback","useLayoutEffect"],"mappings":";;;AAEO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,WAAW,WAAW;AAC1B,MAAI,QAAQ;AACZ,aAAW,SAAS,QAAQ;AAC1B,gBAAY;AACZ,QAAI,WAAW,MAAO;AACtB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,wBAAwB;AACtC,QAAM,CAAC,WAAW,YAAY,IAAIA,MAAAA,SAAgC,IAAI;AACtE,QAAM,CAAC,aAAa,cAAc,IAAIA,MAAAA,SAAgC,IAAI;AAC1E,QAAM,CAAC,cAAc,eAAe,IAAIA,MAAAA,SAAS,QAAQ;AAEzD,QAAM,UAAUC,MAAAA,YAAY,MAAM;AAChC,QAAI,CAAC,aAAa,CAAC,YAAa;AAChC,UAAM,QAAQ,UAAU,sBAAA,EAAwB;AAChD,QAAI,SAAS,EAAG;AAChB,UAAM,CAAC,UAAU,GAAG,QAAQ,IAAI,CAAC,GAAG,YAAY,QAAQ;AACxD,UAAM,UAAU,SAAS,IAAA;AACzB,QAAI,CAAC,YAAY,CAAC;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAEJ,UAAM,QAAQ,sBAAsB;AAAA,MAClC,OAAO,QAAQ;AAAA,MACf,UAAU,SAAS,sBAAA,EAAwB;AAAA,MAC3C,SAAS,QAAQ,sBAAA,EAAwB;AAAA,MACzC,QAAQ,SAAS,IAAI,CAAC,SAAS,KAAK,sBAAA,EAAwB,KAAK;AAAA,IAAA,CAClE;AACD,oBAAgB,CAAC,aAAc,aAAa,QAAQ,WAAW,KAAM;AAAA,EACvE,GAAG,CAAC,WAAW,WAAW,CAAC;AAE3BC,QAAAA,gBAAgB,MAAM;AACpB,YAAA;AAAA,EACF,CAAC;AAEDA,QAAAA,gBAAgB,MAAM;AACpB,QAAI,CAAC,aAAa,CAAC,YAAa;AAChC,YAAA;AACA,UAAM,WACJ,OAAO,mBAAmB,cACtB,SACA,IAAI,eAAe,OAAO;AAChC,yCAAU,QAAQ;AAClB,yCAAU,QAAQ;AAClB,WAAO,iBAAiB,UAAU,OAAO;AACzC,WAAO,MAAM;AACX,2CAAU;AACV,aAAO,oBAAoB,UAAU,OAAO;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,WAAW,aAAa,OAAO,CAAC;AAEpC,SAAO;AAAA,IACL,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB;AAAA,EAAA;AAEJ;;;"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
export declare function runtimeBarInlineCount({ width, identity, trigger, fields,
|
|
1
|
+
export declare function runtimeBarInlineCount({ width, identity, trigger, fields, }: {
|
|
2
2
|
width: number;
|
|
3
3
|
identity: number;
|
|
4
4
|
trigger: number;
|
|
5
5
|
fields: number[];
|
|
6
|
-
gap: number;
|
|
7
6
|
}): number;
|
|
8
7
|
export declare function useRuntimeBarOverflow(): {
|
|
9
8
|
containerRef: import('react').Dispatch<import('react').SetStateAction<HTMLDivElement | null>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-runtime-bar-overflow.d.ts","sourceRoot":"","sources":["../../../src/data/runtime/use-runtime-bar-overflow.ts"],"names":[],"mappings":"AAEA,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,EACR,OAAO,EACP,MAAM,
|
|
1
|
+
{"version":3,"file":"use-runtime-bar-overflow.d.ts","sourceRoot":"","sources":["../../../src/data/runtime/use-runtime-bar-overflow.ts"],"names":[],"mappings":"AAEA,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,EACR,OAAO,EACP,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,UASA;AAED,wBAAgB,qBAAqB;;;;EAiDpC"}
|
|
@@ -3,13 +3,12 @@ function runtimeBarInlineCount({
|
|
|
3
3
|
width,
|
|
4
4
|
identity,
|
|
5
5
|
trigger,
|
|
6
|
-
fields
|
|
7
|
-
gap
|
|
6
|
+
fields
|
|
8
7
|
}) {
|
|
9
|
-
let occupied = identity + trigger
|
|
8
|
+
let occupied = identity + trigger;
|
|
10
9
|
let count = 0;
|
|
11
10
|
for (const field of fields) {
|
|
12
|
-
occupied += field
|
|
11
|
+
occupied += field;
|
|
13
12
|
if (occupied > width) break;
|
|
14
13
|
count++;
|
|
15
14
|
}
|
|
@@ -29,16 +28,11 @@ function useRuntimeBarOverflow() {
|
|
|
29
28
|
throw new Error(
|
|
30
29
|
"Runtime bar measurement requires identity and a menu trigger"
|
|
31
30
|
);
|
|
32
|
-
const spacing = getComputedStyle(measurement).columnGap;
|
|
33
|
-
const gap = spacing === "normal" || spacing === "" ? 0 : Number.parseFloat(spacing);
|
|
34
|
-
if (!Number.isFinite(gap))
|
|
35
|
-
throw new Error(`Invalid runtime bar spacing: ${spacing}`);
|
|
36
31
|
const count = runtimeBarInlineCount({
|
|
37
32
|
width: width - 2,
|
|
38
33
|
identity: identity.getBoundingClientRect().width,
|
|
39
34
|
trigger: trigger.getBoundingClientRect().width,
|
|
40
|
-
fields: controls.map((node) => node.getBoundingClientRect().width)
|
|
41
|
-
gap
|
|
35
|
+
fields: controls.map((node) => node.getBoundingClientRect().width)
|
|
42
36
|
});
|
|
43
37
|
setVisibleCount((previous) => previous === count ? previous : count);
|
|
44
38
|
}, [container, measurement]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-runtime-bar-overflow.js","sources":["../../../src/data/runtime/use-runtime-bar-overflow.ts"],"sourcesContent":["import { useCallback, useLayoutEffect, useState } from \"react\";\n\nexport function runtimeBarInlineCount({\n width,\n identity,\n trigger,\n fields,\n
|
|
1
|
+
{"version":3,"file":"use-runtime-bar-overflow.js","sources":["../../../src/data/runtime/use-runtime-bar-overflow.ts"],"sourcesContent":["import { useCallback, useLayoutEffect, useState } from \"react\";\n\nexport function runtimeBarInlineCount({\n width,\n identity,\n trigger,\n fields,\n}: {\n width: number;\n identity: number;\n trigger: number;\n fields: number[];\n}) {\n let occupied = identity + trigger;\n let count = 0;\n for (const field of fields) {\n occupied += field;\n if (occupied > width) break;\n count++;\n }\n return count;\n}\n\nexport function useRuntimeBarOverflow() {\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [measurement, setMeasurement] = useState<HTMLDivElement | null>(null);\n const [visibleCount, setVisibleCount] = useState(Infinity);\n\n const measure = useCallback(() => {\n if (!container || !measurement) return;\n const width = container.getBoundingClientRect().width;\n if (width <= 0) return;\n const [identity, ...controls] = [...measurement.children];\n const trigger = controls.pop();\n if (!identity || !trigger)\n throw new Error(\n \"Runtime bar measurement requires identity and a menu trigger\",\n );\n const count = runtimeBarInlineCount({\n width: width - 2,\n identity: identity.getBoundingClientRect().width,\n trigger: trigger.getBoundingClientRect().width,\n fields: controls.map((node) => node.getBoundingClientRect().width),\n });\n setVisibleCount((previous) => (previous === count ? previous : count));\n }, [container, measurement]);\n\n useLayoutEffect(() => {\n measure();\n });\n\n useLayoutEffect(() => {\n if (!container || !measurement) return;\n measure();\n const observer =\n typeof ResizeObserver === \"undefined\"\n ? undefined\n : new ResizeObserver(measure);\n observer?.observe(container);\n observer?.observe(measurement);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, [container, measurement, measure]);\n\n return {\n containerRef: setContainer,\n measurementRef: setMeasurement,\n visibleCount,\n };\n}\n"],"names":[],"mappings":";AAEO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,WAAW,WAAW;AAC1B,MAAI,QAAQ;AACZ,aAAW,SAAS,QAAQ;AAC1B,gBAAY;AACZ,QAAI,WAAW,MAAO;AACtB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,wBAAwB;AACtC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAgC,IAAI;AACtE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAgC,IAAI;AAC1E,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,QAAQ;AAEzD,QAAM,UAAU,YAAY,MAAM;AAChC,QAAI,CAAC,aAAa,CAAC,YAAa;AAChC,UAAM,QAAQ,UAAU,sBAAA,EAAwB;AAChD,QAAI,SAAS,EAAG;AAChB,UAAM,CAAC,UAAU,GAAG,QAAQ,IAAI,CAAC,GAAG,YAAY,QAAQ;AACxD,UAAM,UAAU,SAAS,IAAA;AACzB,QAAI,CAAC,YAAY,CAAC;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAEJ,UAAM,QAAQ,sBAAsB;AAAA,MAClC,OAAO,QAAQ;AAAA,MACf,UAAU,SAAS,sBAAA,EAAwB;AAAA,MAC3C,SAAS,QAAQ,sBAAA,EAAwB;AAAA,MACzC,QAAQ,SAAS,IAAI,CAAC,SAAS,KAAK,sBAAA,EAAwB,KAAK;AAAA,IAAA,CAClE;AACD,oBAAgB,CAAC,aAAc,aAAa,QAAQ,WAAW,KAAM;AAAA,EACvE,GAAG,CAAC,WAAW,WAAW,CAAC;AAE3B,kBAAgB,MAAM;AACpB,YAAA;AAAA,EACF,CAAC;AAED,kBAAgB,MAAM;AACpB,QAAI,CAAC,aAAa,CAAC,YAAa;AAChC,YAAA;AACA,UAAM,WACJ,OAAO,mBAAmB,cACtB,SACA,IAAI,eAAe,OAAO;AAChC,yCAAU,QAAQ;AAClB,yCAAU,QAAQ;AAClB,WAAO,iBAAiB,UAAU,OAAO;AACzC,WAAO,MAAM;AACX,2CAAU;AACV,aAAO,oBAAoB,UAAU,OAAO;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,WAAW,aAAa,OAAO,CAAC;AAEpC,SAAO;AAAA,IACL,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB;AAAA,EAAA;AAEJ;"}
|
|
@@ -4,26 +4,6 @@ import { Test } from './types.js';
|
|
|
4
4
|
export declare const completedTests: Test[];
|
|
5
5
|
/** Same forest, but as an in-flight run: some running, some queued. */
|
|
6
6
|
export declare const runningTests: Test[];
|
|
7
|
-
/**
|
|
8
|
-
* A suite whose leaves carry very large payloads: a 6×4 deep object, a
|
|
9
|
-
* 500-row array, and an 800-line log — to stress JsonView, LogViewer, and the
|
|
10
|
-
* detail pane's independent scrolling.
|
|
11
|
-
*/
|
|
12
|
-
export declare const largeDetailTests: Test[];
|
|
13
|
-
/**
|
|
14
|
-
* A large forest (4 roots × depth 3 × breadth 4 = 340 nodes / 256 leaves) whose
|
|
15
|
-
* every leaf carries the LargePayloads-sized detail (deep object + 500-row array
|
|
16
|
-
* + 800-line log). Used for the dialog stress test: a long scrolling tree on the
|
|
17
|
-
* left and very large detail payloads on the right.
|
|
18
|
-
*
|
|
19
|
-
* Depth is 3, not 4: at depth 4 (~1024 leaves) the jsdom unit test auto-expands
|
|
20
|
-
* the failing branches and renders the whole tree, which OOM'd CI's
|
|
21
|
-
* memory-constrained shared vitest worker (a STACK_TRACE_ERROR on the "very
|
|
22
|
-
* large tree" test) once this package's suite grew. 340 nodes still scrolls and
|
|
23
|
-
* stresses the detail panes while staying within the worker's budget; the
|
|
24
|
-
* browser story renders the same fixture without the jsdom memory ceiling.
|
|
25
|
-
*/
|
|
26
|
-
export declare const largeTreeTests: Test[];
|
|
27
7
|
/**
|
|
28
8
|
* Demo adapter proving the registry extension point: nodes named "setup" get a
|
|
29
9
|
* custom detail body, an extra "Context" tab, and a node action — the seam that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestRunner.fixtures.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunner.fixtures.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAIpC,mFAAmF;AACnF,eAAO,MAAM,cAAc,EAAE,IAAI,EAmFhC,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,IAAI,EA+B9B,CAAC;
|
|
1
|
+
{"version":3,"file":"TestRunner.fixtures.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunner.fixtures.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAIpC,mFAAmF;AACnF,eAAO,MAAM,cAAc,EAAE,IAAI,EAmFhC,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,IAAI,EA+B9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,eA0C1B,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Test } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A suite whose leaves carry very large payloads: a 6×4 deep object, a
|
|
4
|
+
* 500-row array, and an 800-line log — to stress JsonView, LogViewer, and the
|
|
5
|
+
* detail pane's independent scrolling.
|
|
6
|
+
*/
|
|
7
|
+
export declare const largeDetailTests: Test[];
|
|
8
|
+
/**
|
|
9
|
+
* A large forest (4 roots × depth 3 × breadth 4 = 340 nodes / 256 leaves) whose
|
|
10
|
+
* every leaf carries the LargePayloads-sized detail (deep object + 500-row array
|
|
11
|
+
* + 800-line log). Used for the dialog stress test: a long scrolling tree on the
|
|
12
|
+
* left and very large detail payloads on the right.
|
|
13
|
+
*
|
|
14
|
+
* Depth is 3, not 4: at depth 4 (~1024 leaves) the jsdom unit test auto-expands
|
|
15
|
+
* the failing branches and renders the whole tree, which OOM'd CI's
|
|
16
|
+
* memory-constrained shared vitest worker (a STACK_TRACE_ERROR on the "very
|
|
17
|
+
* large tree" test) once this package's suite grew. 340 nodes still scrolls and
|
|
18
|
+
* stresses the detail panes while staying within the worker's budget; the
|
|
19
|
+
* browser story renders the same fixture without the jsdom memory ceiling.
|
|
20
|
+
*/
|
|
21
|
+
export declare const largeTreeTests: Test[];
|
|
22
|
+
//# sourceMappingURL=TestRunner.large-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestRunner.large-fixtures.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunner.large-fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AA4CpC;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,IAAI,EA6BlC,CAAC;AAgEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,EAAE,IAAI,EAEhC,CAAC"}
|
|
@@ -3,7 +3,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const react = require("react");
|
|
4
4
|
const DEFAULT_LIMITS = { minScale: 0.2, maxScale: 4 };
|
|
5
5
|
const BUTTON_ZOOM_FACTOR = 1.25;
|
|
6
|
-
const
|
|
6
|
+
const PINCH_ZOOM_RATE = 0.01;
|
|
7
|
+
const WHEEL_NOTCH_PX = 100;
|
|
8
|
+
const WHEEL_ZOOM_RATE = Math.log(BUTTON_ZOOM_FACTOR) / WHEEL_NOTCH_PX;
|
|
9
|
+
const LINE_PX = 16;
|
|
10
|
+
const DOM_DELTA_LINE = 1;
|
|
11
|
+
const DOM_DELTA_PAGE = 2;
|
|
7
12
|
const OVERFLOW_MARGIN = 8;
|
|
8
13
|
const DRAG_THRESHOLD = 4;
|
|
9
14
|
function zoomAround(transform, point, factor, limits = DEFAULT_LIMITS) {
|
|
@@ -53,22 +58,29 @@ function useViewportResize(viewportRef, enabled, refit) {
|
|
|
53
58
|
return () => observer.disconnect();
|
|
54
59
|
}, [viewportRef, enabled, refit]);
|
|
55
60
|
}
|
|
56
|
-
function
|
|
61
|
+
function wheelZoomFactor(event, mode, viewportHeight) {
|
|
62
|
+
const modified = event.ctrlKey || event.metaKey;
|
|
63
|
+
if (!modified && mode === "modifier") return void 0;
|
|
64
|
+
const unit = event.deltaMode === DOM_DELTA_LINE ? LINE_PX : event.deltaMode === DOM_DELTA_PAGE ? viewportHeight : 1;
|
|
65
|
+
const pixels = -event.deltaY * unit;
|
|
66
|
+
if (modified) return Math.exp(pixels * PINCH_ZOOM_RATE);
|
|
67
|
+
const step = Math.log(BUTTON_ZOOM_FACTOR);
|
|
68
|
+
return Math.exp(Math.min(step, Math.max(-step, pixels * WHEEL_ZOOM_RATE)));
|
|
69
|
+
}
|
|
70
|
+
function useWheelZoom(viewportRef, enabled, mode, zoomAt) {
|
|
57
71
|
react.useEffect(() => {
|
|
58
72
|
const viewport = viewportRef.current;
|
|
59
73
|
if (!enabled || !viewport) return;
|
|
60
74
|
const onWheel = (event) => {
|
|
61
|
-
|
|
75
|
+
const factor = wheelZoomFactor(event, mode, viewport.clientHeight);
|
|
76
|
+
if (factor === void 0) return;
|
|
62
77
|
event.preventDefault();
|
|
63
78
|
const rect = viewport.getBoundingClientRect();
|
|
64
|
-
zoomAt(
|
|
65
|
-
{ x: event.clientX - rect.left, y: event.clientY - rect.top },
|
|
66
|
-
Math.exp(-event.deltaY * WHEEL_ZOOM_RATE)
|
|
67
|
-
);
|
|
79
|
+
zoomAt({ x: event.clientX - rect.left, y: event.clientY - rect.top }, factor);
|
|
68
80
|
};
|
|
69
81
|
viewport.addEventListener("wheel", onWheel, { passive: false });
|
|
70
82
|
return () => viewport.removeEventListener("wheel", onWheel);
|
|
71
|
-
}, [viewportRef, enabled, zoomAt]);
|
|
83
|
+
}, [viewportRef, enabled, mode, zoomAt]);
|
|
72
84
|
}
|
|
73
85
|
function useDragPan(panBy) {
|
|
74
86
|
const [panning, setPanning] = react.useState(false);
|
|
@@ -104,7 +116,7 @@ function useDragPan(panBy) {
|
|
|
104
116
|
}
|
|
105
117
|
};
|
|
106
118
|
}
|
|
107
|
-
function usePanZoom({ enabled, contentWidth, contentHeight, opening }) {
|
|
119
|
+
function usePanZoom({ enabled, contentWidth, contentHeight, opening, wheel = "modifier" }) {
|
|
108
120
|
const viewportRef = react.useRef(null);
|
|
109
121
|
const contentRef = react.useRef(null);
|
|
110
122
|
const [transform, setTransform] = react.useState({ x: 0, y: 0, scale: 1 });
|
|
@@ -136,7 +148,7 @@ function usePanZoom({ enabled, contentWidth, contentHeight, opening }) {
|
|
|
136
148
|
setTransform((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));
|
|
137
149
|
});
|
|
138
150
|
useViewportResize(viewportRef, enabled, refit);
|
|
139
|
-
useWheelZoom(viewportRef, enabled, zoomAt);
|
|
151
|
+
useWheelZoom(viewportRef, enabled, wheel, zoomAt);
|
|
140
152
|
const hold = (fit) => () => {
|
|
141
153
|
fitted.current = fit;
|
|
142
154
|
refit();
|
|
@@ -155,5 +167,6 @@ function usePanZoom({ enabled, contentWidth, contentHeight, opening }) {
|
|
|
155
167
|
}
|
|
156
168
|
exports.fitTransform = fitTransform;
|
|
157
169
|
exports.usePanZoom = usePanZoom;
|
|
170
|
+
exports.wheelZoomFactor = wheelZoomFactor;
|
|
158
171
|
exports.zoomAround = zoomAround;
|
|
159
172
|
//# sourceMappingURL=use-pan-zoom.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-pan-zoom.cjs","sources":["../../src/data/use-pan-zoom.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\n\nexport interface PanZoomTransform {\n /** Translation of the content's top-left corner inside the viewport, in px. */\n x: number;\n y: number;\n scale: number;\n}\n\nexport interface PanZoomSize {\n width: number;\n height: number;\n}\n\nexport interface PanZoomLimits {\n minScale: number;\n maxScale: number;\n}\n\nconst DEFAULT_LIMITS: PanZoomLimits = { minScale: 0.2, maxScale: 4 };\nconst BUTTON_ZOOM_FACTOR = 1.25;\nconst WHEEL_ZOOM_RATE = 0.01;\n/** Air kept between overflowing content and the viewport edge when fitting, in content px. */\nconst OVERFLOW_MARGIN = 8;\n/** Pointer travel in px before a press becomes a pan rather than a click. */\nconst DRAG_THRESHOLD = 4;\n\n/** Scales by `factor`, clamped to `limits`, keeping the content under `point` (viewport px) where it is. */\nexport function zoomAround(\n transform: PanZoomTransform,\n point: { x: number; y: number },\n factor: number,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n): PanZoomTransform {\n const scale = Math.min(limits.maxScale, Math.max(limits.minScale, transform.scale * factor));\n const ratio = scale / transform.scale;\n return {\n x: point.x - (point.x - transform.x) * ratio,\n y: point.y - (point.y - transform.y) * ratio,\n scale,\n };\n}\n\nexport interface FitOptions {\n /** The smallest scale the fit shrinks to. Content too large for it overflows the viewport. */\n minScale?: number;\n /** The content point an overflowing axis is centred on. Defaults to the content's centre. */\n focus?: { x: number; y: number };\n}\n\n/** Offset of one axis: centred when the content fits, else centred on `focus` without showing past either edge. */\nfunction fitOffset(viewport: number, content: number, focus: number, scale: number): number {\n const slack = viewport - content * scale;\n return slack >= 0 ? slack / 2 : Math.min(0, Math.max(slack, viewport / 2 - focus * scale));\n}\n\n/**\n * The transform that shows all of `content` centred in `viewport`. Content is\n * only ever shrunk to fit, never enlarged past its natural size. With\n * `fit.minScale` it is not shrunk past that either: it then overflows, centred\n * on `fit.focus`, and the rest is reached by panning. An unmeasured\n * (zero-sized) viewport yields the identity transform.\n */\nexport function fitTransform(\n content: PanZoomSize,\n viewport: PanZoomSize,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n fit: FitOptions = {},\n): PanZoomTransform {\n if (viewport.width <= 0 || viewport.height <= 0 || content.width <= 0 || content.height <= 0) {\n return { x: 0, y: 0, scale: 1 };\n }\n const whole = Math.min(1, viewport.width / content.width, viewport.height / content.height);\n const scale = Math.max(limits.minScale, Math.min(1, fit.minScale ?? 0), whole);\n const focus = fit.focus ?? { x: content.width / 2, y: content.height / 2 };\n return {\n x: fitOffset(viewport.width, content.width, focus.x, scale),\n y: fitOffset(viewport.height, content.height, focus.y, scale),\n scale,\n };\n}\n\nexport interface UsePanZoomOptions {\n /** When false the hook is inert: no listeners, identity transform. */\n enabled: boolean;\n /** Natural (unscaled) size of the content being panned and zoomed. */\n contentWidth: number;\n contentHeight: number;\n /** How the content opens, and what `reset()` returns to. `fit()` ignores it and shows everything. */\n opening?: FitOptions;\n}\n\nexport interface PanZoomViewportHandlers {\n onPointerDown: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerMove: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerUp: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerCancel: (event: ReactPointerEvent<HTMLDivElement>) => void;\n}\n\nexport interface PanZoom {\n /** Attach to the clipping viewport element. */\n viewportRef: RefObject<HTMLDivElement | null>;\n /** Attach to the content element, so anything overflowing its box is fitted too. */\n contentRef: RefObject<HTMLDivElement | null>;\n /** Spread onto the viewport element; empty when disabled. */\n handlers: Partial<PanZoomViewportHandlers>;\n /** Apply to the content as `translate(x, y) scale(scale)` with a top-left transform origin. */\n transform: PanZoomTransform;\n panning: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n /** Shows everything, however small. */\n fit: () => void;\n /** Returns to how the content opened: see `UsePanZoomOptions.opening`. */\n reset: () => void;\n}\n\ntype ViewportRef = RefObject<HTMLDivElement | null>;\n\nfunction viewportSize(element: HTMLElement): PanZoomSize {\n return { width: element.clientWidth, height: element.clientHeight };\n}\n\n/**\n * The content's size, counting children that overflow its box to the right or\n * below (a label hanging off its edge): they extend its scroll size, which a\n * transform leaves untouched.\n */\nfunction overflowedSize(element: HTMLElement | null, natural: PanZoomSize): PanZoomSize {\n const overflowed = (size: number, scrolled: number | undefined) =>\n scrolled !== undefined && scrolled > size ? scrolled + OVERFLOW_MARGIN : size;\n return {\n width: overflowed(natural.width, element?.scrollWidth),\n height: overflowed(natural.height, element?.scrollHeight),\n };\n}\n\n/** Runs `refit` now and whenever the viewport is resized. */\nfunction useViewportResize(viewportRef: ViewportRef, enabled: boolean, refit: () => void): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n refit();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(refit);\n observer.observe(viewport);\n return () => observer.disconnect();\n }, [viewportRef, enabled, refit]);\n}\n\nfunction useWheelZoom(\n viewportRef: ViewportRef,\n enabled: boolean,\n zoomAt: (point: { x: number; y: number }, factor: number) => void,\n): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n const onWheel = (event: WheelEvent) => {\n if (!event.ctrlKey && !event.metaKey) return;\n event.preventDefault();\n const rect = viewport.getBoundingClientRect();\n zoomAt(\n { x: event.clientX - rect.left, y: event.clientY - rect.top },\n Math.exp(-event.deltaY * WHEEL_ZOOM_RATE),\n );\n };\n // React registers wheel listeners as passive, which cannot preventDefault the page zoom.\n viewport.addEventListener(\"wheel\", onWheel, { passive: false });\n return () => viewport.removeEventListener(\"wheel\", onWheel);\n }, [viewportRef, enabled, zoomAt]);\n}\n\nfunction useDragPan(panBy: (dx: number, dy: number) => void): {\n panning: boolean;\n handlers: PanZoomViewportHandlers;\n} {\n const [panning, setPanning] = useState(false);\n const drag = useRef<{ pointerId: number; x: number; y: number; moved: boolean } | null>(null);\n const endDrag = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return;\n drag.current = null;\n setPanning(false);\n };\n return {\n panning,\n handlers: {\n onPointerDown: (event) => {\n if (event.button !== 0) return;\n drag.current = { pointerId: event.pointerId, x: event.clientX, y: event.clientY, moved: false };\n },\n onPointerMove: (event) => {\n const active = drag.current;\n if (!active || active.pointerId !== event.pointerId) return;\n const dx = event.clientX - active.x;\n const dy = event.clientY - active.y;\n if (!active.moved) {\n if (Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n // Capturing only once a drag is under way leaves plain clicks on nodes and edges alone,\n // and retargets the click that ends a drag to the viewport so it selects nothing.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n setPanning(true);\n }\n drag.current = { pointerId: active.pointerId, x: event.clientX, y: event.clientY, moved: true };\n panBy(dx, dy);\n },\n onPointerUp: endDrag,\n onPointerCancel: endDrag,\n },\n };\n}\n\n/**\n * Pan and zoom for a fixed-size content box inside a clipping viewport:\n * ctrl/meta + wheel (which is also what a trackpad pinch sends) zooms around\n * the pointer, dragging pans, and `zoomIn`/`zoomOut`/`fit` back a control\n * cluster. The content stays fitted to the viewport — across content and\n * viewport size changes — until the user pans or zooms, and again after\n * `fit()` or `reset()`.\n */\nexport function usePanZoom({ enabled, contentWidth, contentHeight, opening }: UsePanZoomOptions): PanZoom {\n const viewportRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [transform, setTransform] = useState<PanZoomTransform>({ x: 0, y: 0, scale: 1 });\n // Which fit the content is held to, until the user pans or zooms.\n const fitted = useRef<\"opening\" | \"everything\" | undefined>(\"opening\");\n const { minScale, focus } = opening ?? {};\n const [focusX, focusY] = [focus?.x, focus?.y];\n\n const refit = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport || !fitted.current) return;\n const content = overflowedSize(contentRef.current, { width: contentWidth, height: contentHeight });\n const fit: FitOptions =\n fitted.current === \"everything\"\n ? {}\n : {\n ...(minScale !== undefined ? { minScale } : {}),\n ...(focusX !== undefined && focusY !== undefined ? { focus: { x: focusX, y: focusY } } : {}),\n };\n setTransform(fitTransform(content, viewportSize(viewport), DEFAULT_LIMITS, fit));\n }, [contentWidth, contentHeight, minScale, focusX, focusY]);\n const zoomAt = useCallback((point: { x: number; y: number }, factor: number) => {\n fitted.current = undefined;\n setTransform((current) => zoomAround(current, point, factor));\n }, []);\n const zoomAtCentre = (factor: number) => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n const { width, height } = viewportSize(viewport);\n zoomAt({ x: width / 2, y: height / 2 }, factor);\n };\n const { panning, handlers } = useDragPan((dx, dy) => {\n fitted.current = undefined;\n setTransform((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));\n });\n useViewportResize(viewportRef, enabled, refit);\n useWheelZoom(viewportRef, enabled, zoomAt);\n const hold = (fit: \"opening\" | \"everything\") => () => {\n fitted.current = fit;\n refit();\n };\n\n return {\n viewportRef,\n contentRef,\n handlers: enabled ? handlers : {},\n transform,\n panning,\n zoomIn: () => zoomAtCentre(BUTTON_ZOOM_FACTOR),\n zoomOut: () => zoomAtCentre(1 / BUTTON_ZOOM_FACTOR),\n fit: hold(\"everything\"),\n reset: hold(\"opening\"),\n };\n}\n"],"names":["useEffect","useState","useRef","useCallback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-pan-zoom.cjs","sources":["../../src/data/use-pan-zoom.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\n\nexport interface PanZoomTransform {\n /** Translation of the content's top-left corner inside the viewport, in px. */\n x: number;\n y: number;\n scale: number;\n}\n\nexport interface PanZoomSize {\n width: number;\n height: number;\n}\n\nexport interface PanZoomLimits {\n minScale: number;\n maxScale: number;\n}\n\nconst DEFAULT_LIMITS: PanZoomLimits = { minScale: 0.2, maxScale: 4 };\nconst BUTTON_ZOOM_FACTOR = 1.25;\n/** A pinch sends small ctrl-wheel deltas: this rate keeps it tracking the fingers. */\nconst PINCH_ZOOM_RATE = 0.01;\n/** A mouse wheel notch is about 100px of delta; this rate makes one notch a button step. */\nconst WHEEL_NOTCH_PX = 100;\nconst WHEEL_ZOOM_RATE = Math.log(BUTTON_ZOOM_FACTOR) / WHEEL_NOTCH_PX;\nconst LINE_PX = 16;\nconst DOM_DELTA_LINE = 1;\nconst DOM_DELTA_PAGE = 2;\n/** Air kept between overflowing content and the viewport edge when fitting, in content px. */\nconst OVERFLOW_MARGIN = 8;\n/** Pointer travel in px before a press becomes a pan rather than a click. */\nconst DRAG_THRESHOLD = 4;\n\n/** Scales by `factor`, clamped to `limits`, keeping the content under `point` (viewport px) where it is. */\nexport function zoomAround(\n transform: PanZoomTransform,\n point: { x: number; y: number },\n factor: number,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n): PanZoomTransform {\n const scale = Math.min(limits.maxScale, Math.max(limits.minScale, transform.scale * factor));\n const ratio = scale / transform.scale;\n return {\n x: point.x - (point.x - transform.x) * ratio,\n y: point.y - (point.y - transform.y) * ratio,\n scale,\n };\n}\n\nexport interface FitOptions {\n /** The smallest scale the fit shrinks to. Content too large for it overflows the viewport. */\n minScale?: number;\n /** The content point an overflowing axis is centred on. Defaults to the content's centre. */\n focus?: { x: number; y: number };\n}\n\n/** Offset of one axis: centred when the content fits, else centred on `focus` without showing past either edge. */\nfunction fitOffset(viewport: number, content: number, focus: number, scale: number): number {\n const slack = viewport - content * scale;\n return slack >= 0 ? slack / 2 : Math.min(0, Math.max(slack, viewport / 2 - focus * scale));\n}\n\n/**\n * The transform that shows all of `content` centred in `viewport`. Content is\n * only ever shrunk to fit, never enlarged past its natural size. With\n * `fit.minScale` it is not shrunk past that either: it then overflows, centred\n * on `fit.focus`, and the rest is reached by panning. An unmeasured\n * (zero-sized) viewport yields the identity transform.\n */\nexport function fitTransform(\n content: PanZoomSize,\n viewport: PanZoomSize,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n fit: FitOptions = {},\n): PanZoomTransform {\n if (viewport.width <= 0 || viewport.height <= 0 || content.width <= 0 || content.height <= 0) {\n return { x: 0, y: 0, scale: 1 };\n }\n const whole = Math.min(1, viewport.width / content.width, viewport.height / content.height);\n const scale = Math.max(limits.minScale, Math.min(1, fit.minScale ?? 0), whole);\n const focus = fit.focus ?? { x: content.width / 2, y: content.height / 2 };\n return {\n x: fitOffset(viewport.width, content.width, focus.x, scale),\n y: fitOffset(viewport.height, content.height, focus.y, scale),\n scale,\n };\n}\n\nexport interface UsePanZoomOptions {\n /** When false the hook is inert: no listeners, identity transform. */\n enabled: boolean;\n /** Natural (unscaled) size of the content being panned and zoomed. */\n contentWidth: number;\n contentHeight: number;\n /** How the content opens, and what `reset()` returns to. `fit()` ignores it and shows everything. */\n opening?: FitOptions;\n /** What a plain wheel does; defaults to \"modifier\". See `WheelMode`. */\n wheel?: WheelMode;\n}\n\nexport interface PanZoomViewportHandlers {\n onPointerDown: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerMove: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerUp: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerCancel: (event: ReactPointerEvent<HTMLDivElement>) => void;\n}\n\nexport interface PanZoom {\n /** Attach to the clipping viewport element. */\n viewportRef: RefObject<HTMLDivElement | null>;\n /** Attach to the content element, so anything overflowing its box is fitted too. */\n contentRef: RefObject<HTMLDivElement | null>;\n /** Spread onto the viewport element; empty when disabled. */\n handlers: Partial<PanZoomViewportHandlers>;\n /** Apply to the content as `translate(x, y) scale(scale)` with a top-left transform origin. */\n transform: PanZoomTransform;\n panning: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n /** Shows everything, however small. */\n fit: () => void;\n /** Returns to how the content opened: see `UsePanZoomOptions.opening`. */\n reset: () => void;\n}\n\ntype ViewportRef = RefObject<HTMLDivElement | null>;\n\nfunction viewportSize(element: HTMLElement): PanZoomSize {\n return { width: element.clientWidth, height: element.clientHeight };\n}\n\n/**\n * The content's size, counting children that overflow its box to the right or\n * below (a label hanging off its edge): they extend its scroll size, which a\n * transform leaves untouched.\n */\nfunction overflowedSize(element: HTMLElement | null, natural: PanZoomSize): PanZoomSize {\n const overflowed = (size: number, scrolled: number | undefined) =>\n scrolled !== undefined && scrolled > size ? scrolled + OVERFLOW_MARGIN : size;\n return {\n width: overflowed(natural.width, element?.scrollWidth),\n height: overflowed(natural.height, element?.scrollHeight),\n };\n}\n\n/** Runs `refit` now and whenever the viewport is resized. */\nfunction useViewportResize(viewportRef: ViewportRef, enabled: boolean, refit: () => void): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n refit();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(refit);\n observer.observe(viewport);\n return () => observer.disconnect();\n }, [viewportRef, enabled, refit]);\n}\n\n/**\n * What a plain (unmodified) wheel does over the viewport: \"modifier\" leaves it to scroll the page\n * and zooms only on ctrl/meta + wheel or a pinch; \"zoom\" zooms on every wheel.\n */\nexport type WheelMode = \"modifier\" | \"zoom\";\n\ntype WheelInput = Pick<WheelEvent, \"deltaY\" | \"deltaMode\" | \"ctrlKey\" | \"metaKey\">;\n\n/**\n * The scale factor a wheel event zooms by, or undefined when `mode` leaves it to the page. The\n * delta is read in pixels whatever its unit, and a plain wheel notch is capped at one button step.\n */\nexport function wheelZoomFactor(event: WheelInput, mode: WheelMode, viewportHeight: number): number | undefined {\n const modified = event.ctrlKey || event.metaKey;\n if (!modified && mode === \"modifier\") return undefined;\n const unit = event.deltaMode === DOM_DELTA_LINE ? LINE_PX : event.deltaMode === DOM_DELTA_PAGE ? viewportHeight : 1;\n const pixels = -event.deltaY * unit;\n if (modified) return Math.exp(pixels * PINCH_ZOOM_RATE);\n const step = Math.log(BUTTON_ZOOM_FACTOR);\n return Math.exp(Math.min(step, Math.max(-step, pixels * WHEEL_ZOOM_RATE)));\n}\n\nfunction useWheelZoom(\n viewportRef: ViewportRef,\n enabled: boolean,\n mode: WheelMode,\n zoomAt: (point: { x: number; y: number }, factor: number) => void,\n): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n const onWheel = (event: WheelEvent) => {\n const factor = wheelZoomFactor(event, mode, viewport.clientHeight);\n if (factor === undefined) return;\n event.preventDefault();\n const rect = viewport.getBoundingClientRect();\n zoomAt({ x: event.clientX - rect.left, y: event.clientY - rect.top }, factor);\n };\n // React registers wheel listeners as passive, which cannot preventDefault the page zoom or scroll.\n viewport.addEventListener(\"wheel\", onWheel, { passive: false });\n return () => viewport.removeEventListener(\"wheel\", onWheel);\n }, [viewportRef, enabled, mode, zoomAt]);\n}\n\nfunction useDragPan(panBy: (dx: number, dy: number) => void): {\n panning: boolean;\n handlers: PanZoomViewportHandlers;\n} {\n const [panning, setPanning] = useState(false);\n const drag = useRef<{ pointerId: number; x: number; y: number; moved: boolean } | null>(null);\n const endDrag = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return;\n drag.current = null;\n setPanning(false);\n };\n return {\n panning,\n handlers: {\n onPointerDown: (event) => {\n if (event.button !== 0) return;\n drag.current = { pointerId: event.pointerId, x: event.clientX, y: event.clientY, moved: false };\n },\n onPointerMove: (event) => {\n const active = drag.current;\n if (!active || active.pointerId !== event.pointerId) return;\n const dx = event.clientX - active.x;\n const dy = event.clientY - active.y;\n if (!active.moved) {\n if (Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n // Capturing only once a drag is under way leaves plain clicks on nodes and edges alone,\n // and retargets the click that ends a drag to the viewport so it selects nothing.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n setPanning(true);\n }\n drag.current = { pointerId: active.pointerId, x: event.clientX, y: event.clientY, moved: true };\n panBy(dx, dy);\n },\n onPointerUp: endDrag,\n onPointerCancel: endDrag,\n },\n };\n}\n\n/**\n * Pan and zoom for a fixed-size content box inside a clipping viewport:\n * ctrl/meta + wheel (which is also what a trackpad pinch sends) — or any wheel, with `wheel: \"zoom\"` — zooms around\n * the pointer, dragging pans, and `zoomIn`/`zoomOut`/`fit` back a control\n * cluster. The content stays fitted to the viewport — across content and\n * viewport size changes — until the user pans or zooms, and again after\n * `fit()` or `reset()`.\n */\nexport function usePanZoom({ enabled, contentWidth, contentHeight, opening, wheel = \"modifier\" }: UsePanZoomOptions): PanZoom {\n const viewportRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [transform, setTransform] = useState<PanZoomTransform>({ x: 0, y: 0, scale: 1 });\n // Which fit the content is held to, until the user pans or zooms.\n const fitted = useRef<\"opening\" | \"everything\" | undefined>(\"opening\");\n const { minScale, focus } = opening ?? {};\n const [focusX, focusY] = [focus?.x, focus?.y];\n\n const refit = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport || !fitted.current) return;\n const content = overflowedSize(contentRef.current, { width: contentWidth, height: contentHeight });\n const fit: FitOptions =\n fitted.current === \"everything\"\n ? {}\n : {\n ...(minScale !== undefined ? { minScale } : {}),\n ...(focusX !== undefined && focusY !== undefined ? { focus: { x: focusX, y: focusY } } : {}),\n };\n setTransform(fitTransform(content, viewportSize(viewport), DEFAULT_LIMITS, fit));\n }, [contentWidth, contentHeight, minScale, focusX, focusY]);\n const zoomAt = useCallback((point: { x: number; y: number }, factor: number) => {\n fitted.current = undefined;\n setTransform((current) => zoomAround(current, point, factor));\n }, []);\n const zoomAtCentre = (factor: number) => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n const { width, height } = viewportSize(viewport);\n zoomAt({ x: width / 2, y: height / 2 }, factor);\n };\n const { panning, handlers } = useDragPan((dx, dy) => {\n fitted.current = undefined;\n setTransform((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));\n });\n useViewportResize(viewportRef, enabled, refit);\n useWheelZoom(viewportRef, enabled, wheel, zoomAt);\n const hold = (fit: \"opening\" | \"everything\") => () => {\n fitted.current = fit;\n refit();\n };\n\n return {\n viewportRef,\n contentRef,\n handlers: enabled ? handlers : {},\n transform,\n panning,\n zoomIn: () => zoomAtCentre(BUTTON_ZOOM_FACTOR),\n zoomOut: () => zoomAtCentre(1 / BUTTON_ZOOM_FACTOR),\n fit: hold(\"everything\"),\n reset: hold(\"opening\"),\n };\n}\n"],"names":["useEffect","useState","useRef","useCallback"],"mappings":"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"}
|
|
@@ -43,6 +43,8 @@ export interface UsePanZoomOptions {
|
|
|
43
43
|
contentHeight: number;
|
|
44
44
|
/** How the content opens, and what `reset()` returns to. `fit()` ignores it and shows everything. */
|
|
45
45
|
opening?: FitOptions;
|
|
46
|
+
/** What a plain wheel does; defaults to "modifier". See `WheelMode`. */
|
|
47
|
+
wheel?: WheelMode;
|
|
46
48
|
}
|
|
47
49
|
export interface PanZoomViewportHandlers {
|
|
48
50
|
onPointerDown: (event: ReactPointerEvent<HTMLDivElement>) => void;
|
|
@@ -67,13 +69,25 @@ export interface PanZoom {
|
|
|
67
69
|
/** Returns to how the content opened: see `UsePanZoomOptions.opening`. */
|
|
68
70
|
reset: () => void;
|
|
69
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* What a plain (unmodified) wheel does over the viewport: "modifier" leaves it to scroll the page
|
|
74
|
+
* and zooms only on ctrl/meta + wheel or a pinch; "zoom" zooms on every wheel.
|
|
75
|
+
*/
|
|
76
|
+
export type WheelMode = "modifier" | "zoom";
|
|
77
|
+
type WheelInput = Pick<WheelEvent, "deltaY" | "deltaMode" | "ctrlKey" | "metaKey">;
|
|
78
|
+
/**
|
|
79
|
+
* The scale factor a wheel event zooms by, or undefined when `mode` leaves it to the page. The
|
|
80
|
+
* delta is read in pixels whatever its unit, and a plain wheel notch is capped at one button step.
|
|
81
|
+
*/
|
|
82
|
+
export declare function wheelZoomFactor(event: WheelInput, mode: WheelMode, viewportHeight: number): number | undefined;
|
|
70
83
|
/**
|
|
71
84
|
* Pan and zoom for a fixed-size content box inside a clipping viewport:
|
|
72
|
-
* ctrl/meta + wheel (which is also what a trackpad pinch sends) zooms around
|
|
85
|
+
* ctrl/meta + wheel (which is also what a trackpad pinch sends) — or any wheel, with `wheel: "zoom"` — zooms around
|
|
73
86
|
* the pointer, dragging pans, and `zoomIn`/`zoomOut`/`fit` back a control
|
|
74
87
|
* cluster. The content stays fitted to the viewport — across content and
|
|
75
88
|
* viewport size changes — until the user pans or zooms, and again after
|
|
76
89
|
* `fit()` or `reset()`.
|
|
77
90
|
*/
|
|
78
|
-
export declare function usePanZoom({ enabled, contentWidth, contentHeight, opening }: UsePanZoomOptions): PanZoom;
|
|
91
|
+
export declare function usePanZoom({ enabled, contentWidth, contentHeight, opening, wheel }: UsePanZoomOptions): PanZoom;
|
|
92
|
+
export {};
|
|
79
93
|
//# sourceMappingURL=use-pan-zoom.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-pan-zoom.d.ts","sourceRoot":"","sources":["../../src/data/use-pan-zoom.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,YAAY,IAAI,iBAAiB,EACtC,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,gBAAgB;IAC/B,+EAA+E;IAC/E,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"use-pan-zoom.d.ts","sourceRoot":"","sources":["../../src/data/use-pan-zoom.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { useRef, useState, useCallback, useEffect } from "react";
|
|
2
2
|
const DEFAULT_LIMITS = { minScale: 0.2, maxScale: 4 };
|
|
3
3
|
const BUTTON_ZOOM_FACTOR = 1.25;
|
|
4
|
-
const
|
|
4
|
+
const PINCH_ZOOM_RATE = 0.01;
|
|
5
|
+
const WHEEL_NOTCH_PX = 100;
|
|
6
|
+
const WHEEL_ZOOM_RATE = Math.log(BUTTON_ZOOM_FACTOR) / WHEEL_NOTCH_PX;
|
|
7
|
+
const LINE_PX = 16;
|
|
8
|
+
const DOM_DELTA_LINE = 1;
|
|
9
|
+
const DOM_DELTA_PAGE = 2;
|
|
5
10
|
const OVERFLOW_MARGIN = 8;
|
|
6
11
|
const DRAG_THRESHOLD = 4;
|
|
7
12
|
function zoomAround(transform, point, factor, limits = DEFAULT_LIMITS) {
|
|
@@ -51,22 +56,29 @@ function useViewportResize(viewportRef, enabled, refit) {
|
|
|
51
56
|
return () => observer.disconnect();
|
|
52
57
|
}, [viewportRef, enabled, refit]);
|
|
53
58
|
}
|
|
54
|
-
function
|
|
59
|
+
function wheelZoomFactor(event, mode, viewportHeight) {
|
|
60
|
+
const modified = event.ctrlKey || event.metaKey;
|
|
61
|
+
if (!modified && mode === "modifier") return void 0;
|
|
62
|
+
const unit = event.deltaMode === DOM_DELTA_LINE ? LINE_PX : event.deltaMode === DOM_DELTA_PAGE ? viewportHeight : 1;
|
|
63
|
+
const pixels = -event.deltaY * unit;
|
|
64
|
+
if (modified) return Math.exp(pixels * PINCH_ZOOM_RATE);
|
|
65
|
+
const step = Math.log(BUTTON_ZOOM_FACTOR);
|
|
66
|
+
return Math.exp(Math.min(step, Math.max(-step, pixels * WHEEL_ZOOM_RATE)));
|
|
67
|
+
}
|
|
68
|
+
function useWheelZoom(viewportRef, enabled, mode, zoomAt) {
|
|
55
69
|
useEffect(() => {
|
|
56
70
|
const viewport = viewportRef.current;
|
|
57
71
|
if (!enabled || !viewport) return;
|
|
58
72
|
const onWheel = (event) => {
|
|
59
|
-
|
|
73
|
+
const factor = wheelZoomFactor(event, mode, viewport.clientHeight);
|
|
74
|
+
if (factor === void 0) return;
|
|
60
75
|
event.preventDefault();
|
|
61
76
|
const rect = viewport.getBoundingClientRect();
|
|
62
|
-
zoomAt(
|
|
63
|
-
{ x: event.clientX - rect.left, y: event.clientY - rect.top },
|
|
64
|
-
Math.exp(-event.deltaY * WHEEL_ZOOM_RATE)
|
|
65
|
-
);
|
|
77
|
+
zoomAt({ x: event.clientX - rect.left, y: event.clientY - rect.top }, factor);
|
|
66
78
|
};
|
|
67
79
|
viewport.addEventListener("wheel", onWheel, { passive: false });
|
|
68
80
|
return () => viewport.removeEventListener("wheel", onWheel);
|
|
69
|
-
}, [viewportRef, enabled, zoomAt]);
|
|
81
|
+
}, [viewportRef, enabled, mode, zoomAt]);
|
|
70
82
|
}
|
|
71
83
|
function useDragPan(panBy) {
|
|
72
84
|
const [panning, setPanning] = useState(false);
|
|
@@ -102,7 +114,7 @@ function useDragPan(panBy) {
|
|
|
102
114
|
}
|
|
103
115
|
};
|
|
104
116
|
}
|
|
105
|
-
function usePanZoom({ enabled, contentWidth, contentHeight, opening }) {
|
|
117
|
+
function usePanZoom({ enabled, contentWidth, contentHeight, opening, wheel = "modifier" }) {
|
|
106
118
|
const viewportRef = useRef(null);
|
|
107
119
|
const contentRef = useRef(null);
|
|
108
120
|
const [transform, setTransform] = useState({ x: 0, y: 0, scale: 1 });
|
|
@@ -134,7 +146,7 @@ function usePanZoom({ enabled, contentWidth, contentHeight, opening }) {
|
|
|
134
146
|
setTransform((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));
|
|
135
147
|
});
|
|
136
148
|
useViewportResize(viewportRef, enabled, refit);
|
|
137
|
-
useWheelZoom(viewportRef, enabled, zoomAt);
|
|
149
|
+
useWheelZoom(viewportRef, enabled, wheel, zoomAt);
|
|
138
150
|
const hold = (fit) => () => {
|
|
139
151
|
fitted.current = fit;
|
|
140
152
|
refit();
|
|
@@ -154,6 +166,7 @@ function usePanZoom({ enabled, contentWidth, contentHeight, opening }) {
|
|
|
154
166
|
export {
|
|
155
167
|
fitTransform,
|
|
156
168
|
usePanZoom,
|
|
169
|
+
wheelZoomFactor,
|
|
157
170
|
zoomAround
|
|
158
171
|
};
|
|
159
172
|
//# sourceMappingURL=use-pan-zoom.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-pan-zoom.js","sources":["../../src/data/use-pan-zoom.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\n\nexport interface PanZoomTransform {\n /** Translation of the content's top-left corner inside the viewport, in px. */\n x: number;\n y: number;\n scale: number;\n}\n\nexport interface PanZoomSize {\n width: number;\n height: number;\n}\n\nexport interface PanZoomLimits {\n minScale: number;\n maxScale: number;\n}\n\nconst DEFAULT_LIMITS: PanZoomLimits = { minScale: 0.2, maxScale: 4 };\nconst BUTTON_ZOOM_FACTOR = 1.25;\nconst WHEEL_ZOOM_RATE = 0.01;\n/** Air kept between overflowing content and the viewport edge when fitting, in content px. */\nconst OVERFLOW_MARGIN = 8;\n/** Pointer travel in px before a press becomes a pan rather than a click. */\nconst DRAG_THRESHOLD = 4;\n\n/** Scales by `factor`, clamped to `limits`, keeping the content under `point` (viewport px) where it is. */\nexport function zoomAround(\n transform: PanZoomTransform,\n point: { x: number; y: number },\n factor: number,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n): PanZoomTransform {\n const scale = Math.min(limits.maxScale, Math.max(limits.minScale, transform.scale * factor));\n const ratio = scale / transform.scale;\n return {\n x: point.x - (point.x - transform.x) * ratio,\n y: point.y - (point.y - transform.y) * ratio,\n scale,\n };\n}\n\nexport interface FitOptions {\n /** The smallest scale the fit shrinks to. Content too large for it overflows the viewport. */\n minScale?: number;\n /** The content point an overflowing axis is centred on. Defaults to the content's centre. */\n focus?: { x: number; y: number };\n}\n\n/** Offset of one axis: centred when the content fits, else centred on `focus` without showing past either edge. */\nfunction fitOffset(viewport: number, content: number, focus: number, scale: number): number {\n const slack = viewport - content * scale;\n return slack >= 0 ? slack / 2 : Math.min(0, Math.max(slack, viewport / 2 - focus * scale));\n}\n\n/**\n * The transform that shows all of `content` centred in `viewport`. Content is\n * only ever shrunk to fit, never enlarged past its natural size. With\n * `fit.minScale` it is not shrunk past that either: it then overflows, centred\n * on `fit.focus`, and the rest is reached by panning. An unmeasured\n * (zero-sized) viewport yields the identity transform.\n */\nexport function fitTransform(\n content: PanZoomSize,\n viewport: PanZoomSize,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n fit: FitOptions = {},\n): PanZoomTransform {\n if (viewport.width <= 0 || viewport.height <= 0 || content.width <= 0 || content.height <= 0) {\n return { x: 0, y: 0, scale: 1 };\n }\n const whole = Math.min(1, viewport.width / content.width, viewport.height / content.height);\n const scale = Math.max(limits.minScale, Math.min(1, fit.minScale ?? 0), whole);\n const focus = fit.focus ?? { x: content.width / 2, y: content.height / 2 };\n return {\n x: fitOffset(viewport.width, content.width, focus.x, scale),\n y: fitOffset(viewport.height, content.height, focus.y, scale),\n scale,\n };\n}\n\nexport interface UsePanZoomOptions {\n /** When false the hook is inert: no listeners, identity transform. */\n enabled: boolean;\n /** Natural (unscaled) size of the content being panned and zoomed. */\n contentWidth: number;\n contentHeight: number;\n /** How the content opens, and what `reset()` returns to. `fit()` ignores it and shows everything. */\n opening?: FitOptions;\n}\n\nexport interface PanZoomViewportHandlers {\n onPointerDown: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerMove: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerUp: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerCancel: (event: ReactPointerEvent<HTMLDivElement>) => void;\n}\n\nexport interface PanZoom {\n /** Attach to the clipping viewport element. */\n viewportRef: RefObject<HTMLDivElement | null>;\n /** Attach to the content element, so anything overflowing its box is fitted too. */\n contentRef: RefObject<HTMLDivElement | null>;\n /** Spread onto the viewport element; empty when disabled. */\n handlers: Partial<PanZoomViewportHandlers>;\n /** Apply to the content as `translate(x, y) scale(scale)` with a top-left transform origin. */\n transform: PanZoomTransform;\n panning: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n /** Shows everything, however small. */\n fit: () => void;\n /** Returns to how the content opened: see `UsePanZoomOptions.opening`. */\n reset: () => void;\n}\n\ntype ViewportRef = RefObject<HTMLDivElement | null>;\n\nfunction viewportSize(element: HTMLElement): PanZoomSize {\n return { width: element.clientWidth, height: element.clientHeight };\n}\n\n/**\n * The content's size, counting children that overflow its box to the right or\n * below (a label hanging off its edge): they extend its scroll size, which a\n * transform leaves untouched.\n */\nfunction overflowedSize(element: HTMLElement | null, natural: PanZoomSize): PanZoomSize {\n const overflowed = (size: number, scrolled: number | undefined) =>\n scrolled !== undefined && scrolled > size ? scrolled + OVERFLOW_MARGIN : size;\n return {\n width: overflowed(natural.width, element?.scrollWidth),\n height: overflowed(natural.height, element?.scrollHeight),\n };\n}\n\n/** Runs `refit` now and whenever the viewport is resized. */\nfunction useViewportResize(viewportRef: ViewportRef, enabled: boolean, refit: () => void): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n refit();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(refit);\n observer.observe(viewport);\n return () => observer.disconnect();\n }, [viewportRef, enabled, refit]);\n}\n\nfunction useWheelZoom(\n viewportRef: ViewportRef,\n enabled: boolean,\n zoomAt: (point: { x: number; y: number }, factor: number) => void,\n): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n const onWheel = (event: WheelEvent) => {\n if (!event.ctrlKey && !event.metaKey) return;\n event.preventDefault();\n const rect = viewport.getBoundingClientRect();\n zoomAt(\n { x: event.clientX - rect.left, y: event.clientY - rect.top },\n Math.exp(-event.deltaY * WHEEL_ZOOM_RATE),\n );\n };\n // React registers wheel listeners as passive, which cannot preventDefault the page zoom.\n viewport.addEventListener(\"wheel\", onWheel, { passive: false });\n return () => viewport.removeEventListener(\"wheel\", onWheel);\n }, [viewportRef, enabled, zoomAt]);\n}\n\nfunction useDragPan(panBy: (dx: number, dy: number) => void): {\n panning: boolean;\n handlers: PanZoomViewportHandlers;\n} {\n const [panning, setPanning] = useState(false);\n const drag = useRef<{ pointerId: number; x: number; y: number; moved: boolean } | null>(null);\n const endDrag = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return;\n drag.current = null;\n setPanning(false);\n };\n return {\n panning,\n handlers: {\n onPointerDown: (event) => {\n if (event.button !== 0) return;\n drag.current = { pointerId: event.pointerId, x: event.clientX, y: event.clientY, moved: false };\n },\n onPointerMove: (event) => {\n const active = drag.current;\n if (!active || active.pointerId !== event.pointerId) return;\n const dx = event.clientX - active.x;\n const dy = event.clientY - active.y;\n if (!active.moved) {\n if (Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n // Capturing only once a drag is under way leaves plain clicks on nodes and edges alone,\n // and retargets the click that ends a drag to the viewport so it selects nothing.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n setPanning(true);\n }\n drag.current = { pointerId: active.pointerId, x: event.clientX, y: event.clientY, moved: true };\n panBy(dx, dy);\n },\n onPointerUp: endDrag,\n onPointerCancel: endDrag,\n },\n };\n}\n\n/**\n * Pan and zoom for a fixed-size content box inside a clipping viewport:\n * ctrl/meta + wheel (which is also what a trackpad pinch sends) zooms around\n * the pointer, dragging pans, and `zoomIn`/`zoomOut`/`fit` back a control\n * cluster. The content stays fitted to the viewport — across content and\n * viewport size changes — until the user pans or zooms, and again after\n * `fit()` or `reset()`.\n */\nexport function usePanZoom({ enabled, contentWidth, contentHeight, opening }: UsePanZoomOptions): PanZoom {\n const viewportRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [transform, setTransform] = useState<PanZoomTransform>({ x: 0, y: 0, scale: 1 });\n // Which fit the content is held to, until the user pans or zooms.\n const fitted = useRef<\"opening\" | \"everything\" | undefined>(\"opening\");\n const { minScale, focus } = opening ?? {};\n const [focusX, focusY] = [focus?.x, focus?.y];\n\n const refit = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport || !fitted.current) return;\n const content = overflowedSize(contentRef.current, { width: contentWidth, height: contentHeight });\n const fit: FitOptions =\n fitted.current === \"everything\"\n ? {}\n : {\n ...(minScale !== undefined ? { minScale } : {}),\n ...(focusX !== undefined && focusY !== undefined ? { focus: { x: focusX, y: focusY } } : {}),\n };\n setTransform(fitTransform(content, viewportSize(viewport), DEFAULT_LIMITS, fit));\n }, [contentWidth, contentHeight, minScale, focusX, focusY]);\n const zoomAt = useCallback((point: { x: number; y: number }, factor: number) => {\n fitted.current = undefined;\n setTransform((current) => zoomAround(current, point, factor));\n }, []);\n const zoomAtCentre = (factor: number) => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n const { width, height } = viewportSize(viewport);\n zoomAt({ x: width / 2, y: height / 2 }, factor);\n };\n const { panning, handlers } = useDragPan((dx, dy) => {\n fitted.current = undefined;\n setTransform((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));\n });\n useViewportResize(viewportRef, enabled, refit);\n useWheelZoom(viewportRef, enabled, zoomAt);\n const hold = (fit: \"opening\" | \"everything\") => () => {\n fitted.current = fit;\n refit();\n };\n\n return {\n viewportRef,\n contentRef,\n handlers: enabled ? handlers : {},\n transform,\n panning,\n zoomIn: () => zoomAtCentre(BUTTON_ZOOM_FACTOR),\n zoomOut: () => zoomAtCentre(1 / BUTTON_ZOOM_FACTOR),\n fit: hold(\"everything\"),\n reset: hold(\"opening\"),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-pan-zoom.js","sources":["../../src/data/use-pan-zoom.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\n\nexport interface PanZoomTransform {\n /** Translation of the content's top-left corner inside the viewport, in px. */\n x: number;\n y: number;\n scale: number;\n}\n\nexport interface PanZoomSize {\n width: number;\n height: number;\n}\n\nexport interface PanZoomLimits {\n minScale: number;\n maxScale: number;\n}\n\nconst DEFAULT_LIMITS: PanZoomLimits = { minScale: 0.2, maxScale: 4 };\nconst BUTTON_ZOOM_FACTOR = 1.25;\n/** A pinch sends small ctrl-wheel deltas: this rate keeps it tracking the fingers. */\nconst PINCH_ZOOM_RATE = 0.01;\n/** A mouse wheel notch is about 100px of delta; this rate makes one notch a button step. */\nconst WHEEL_NOTCH_PX = 100;\nconst WHEEL_ZOOM_RATE = Math.log(BUTTON_ZOOM_FACTOR) / WHEEL_NOTCH_PX;\nconst LINE_PX = 16;\nconst DOM_DELTA_LINE = 1;\nconst DOM_DELTA_PAGE = 2;\n/** Air kept between overflowing content and the viewport edge when fitting, in content px. */\nconst OVERFLOW_MARGIN = 8;\n/** Pointer travel in px before a press becomes a pan rather than a click. */\nconst DRAG_THRESHOLD = 4;\n\n/** Scales by `factor`, clamped to `limits`, keeping the content under `point` (viewport px) where it is. */\nexport function zoomAround(\n transform: PanZoomTransform,\n point: { x: number; y: number },\n factor: number,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n): PanZoomTransform {\n const scale = Math.min(limits.maxScale, Math.max(limits.minScale, transform.scale * factor));\n const ratio = scale / transform.scale;\n return {\n x: point.x - (point.x - transform.x) * ratio,\n y: point.y - (point.y - transform.y) * ratio,\n scale,\n };\n}\n\nexport interface FitOptions {\n /** The smallest scale the fit shrinks to. Content too large for it overflows the viewport. */\n minScale?: number;\n /** The content point an overflowing axis is centred on. Defaults to the content's centre. */\n focus?: { x: number; y: number };\n}\n\n/** Offset of one axis: centred when the content fits, else centred on `focus` without showing past either edge. */\nfunction fitOffset(viewport: number, content: number, focus: number, scale: number): number {\n const slack = viewport - content * scale;\n return slack >= 0 ? slack / 2 : Math.min(0, Math.max(slack, viewport / 2 - focus * scale));\n}\n\n/**\n * The transform that shows all of `content` centred in `viewport`. Content is\n * only ever shrunk to fit, never enlarged past its natural size. With\n * `fit.minScale` it is not shrunk past that either: it then overflows, centred\n * on `fit.focus`, and the rest is reached by panning. An unmeasured\n * (zero-sized) viewport yields the identity transform.\n */\nexport function fitTransform(\n content: PanZoomSize,\n viewport: PanZoomSize,\n limits: PanZoomLimits = DEFAULT_LIMITS,\n fit: FitOptions = {},\n): PanZoomTransform {\n if (viewport.width <= 0 || viewport.height <= 0 || content.width <= 0 || content.height <= 0) {\n return { x: 0, y: 0, scale: 1 };\n }\n const whole = Math.min(1, viewport.width / content.width, viewport.height / content.height);\n const scale = Math.max(limits.minScale, Math.min(1, fit.minScale ?? 0), whole);\n const focus = fit.focus ?? { x: content.width / 2, y: content.height / 2 };\n return {\n x: fitOffset(viewport.width, content.width, focus.x, scale),\n y: fitOffset(viewport.height, content.height, focus.y, scale),\n scale,\n };\n}\n\nexport interface UsePanZoomOptions {\n /** When false the hook is inert: no listeners, identity transform. */\n enabled: boolean;\n /** Natural (unscaled) size of the content being panned and zoomed. */\n contentWidth: number;\n contentHeight: number;\n /** How the content opens, and what `reset()` returns to. `fit()` ignores it and shows everything. */\n opening?: FitOptions;\n /** What a plain wheel does; defaults to \"modifier\". See `WheelMode`. */\n wheel?: WheelMode;\n}\n\nexport interface PanZoomViewportHandlers {\n onPointerDown: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerMove: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerUp: (event: ReactPointerEvent<HTMLDivElement>) => void;\n onPointerCancel: (event: ReactPointerEvent<HTMLDivElement>) => void;\n}\n\nexport interface PanZoom {\n /** Attach to the clipping viewport element. */\n viewportRef: RefObject<HTMLDivElement | null>;\n /** Attach to the content element, so anything overflowing its box is fitted too. */\n contentRef: RefObject<HTMLDivElement | null>;\n /** Spread onto the viewport element; empty when disabled. */\n handlers: Partial<PanZoomViewportHandlers>;\n /** Apply to the content as `translate(x, y) scale(scale)` with a top-left transform origin. */\n transform: PanZoomTransform;\n panning: boolean;\n zoomIn: () => void;\n zoomOut: () => void;\n /** Shows everything, however small. */\n fit: () => void;\n /** Returns to how the content opened: see `UsePanZoomOptions.opening`. */\n reset: () => void;\n}\n\ntype ViewportRef = RefObject<HTMLDivElement | null>;\n\nfunction viewportSize(element: HTMLElement): PanZoomSize {\n return { width: element.clientWidth, height: element.clientHeight };\n}\n\n/**\n * The content's size, counting children that overflow its box to the right or\n * below (a label hanging off its edge): they extend its scroll size, which a\n * transform leaves untouched.\n */\nfunction overflowedSize(element: HTMLElement | null, natural: PanZoomSize): PanZoomSize {\n const overflowed = (size: number, scrolled: number | undefined) =>\n scrolled !== undefined && scrolled > size ? scrolled + OVERFLOW_MARGIN : size;\n return {\n width: overflowed(natural.width, element?.scrollWidth),\n height: overflowed(natural.height, element?.scrollHeight),\n };\n}\n\n/** Runs `refit` now and whenever the viewport is resized. */\nfunction useViewportResize(viewportRef: ViewportRef, enabled: boolean, refit: () => void): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n refit();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(refit);\n observer.observe(viewport);\n return () => observer.disconnect();\n }, [viewportRef, enabled, refit]);\n}\n\n/**\n * What a plain (unmodified) wheel does over the viewport: \"modifier\" leaves it to scroll the page\n * and zooms only on ctrl/meta + wheel or a pinch; \"zoom\" zooms on every wheel.\n */\nexport type WheelMode = \"modifier\" | \"zoom\";\n\ntype WheelInput = Pick<WheelEvent, \"deltaY\" | \"deltaMode\" | \"ctrlKey\" | \"metaKey\">;\n\n/**\n * The scale factor a wheel event zooms by, or undefined when `mode` leaves it to the page. The\n * delta is read in pixels whatever its unit, and a plain wheel notch is capped at one button step.\n */\nexport function wheelZoomFactor(event: WheelInput, mode: WheelMode, viewportHeight: number): number | undefined {\n const modified = event.ctrlKey || event.metaKey;\n if (!modified && mode === \"modifier\") return undefined;\n const unit = event.deltaMode === DOM_DELTA_LINE ? LINE_PX : event.deltaMode === DOM_DELTA_PAGE ? viewportHeight : 1;\n const pixels = -event.deltaY * unit;\n if (modified) return Math.exp(pixels * PINCH_ZOOM_RATE);\n const step = Math.log(BUTTON_ZOOM_FACTOR);\n return Math.exp(Math.min(step, Math.max(-step, pixels * WHEEL_ZOOM_RATE)));\n}\n\nfunction useWheelZoom(\n viewportRef: ViewportRef,\n enabled: boolean,\n mode: WheelMode,\n zoomAt: (point: { x: number; y: number }, factor: number) => void,\n): void {\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n const onWheel = (event: WheelEvent) => {\n const factor = wheelZoomFactor(event, mode, viewport.clientHeight);\n if (factor === undefined) return;\n event.preventDefault();\n const rect = viewport.getBoundingClientRect();\n zoomAt({ x: event.clientX - rect.left, y: event.clientY - rect.top }, factor);\n };\n // React registers wheel listeners as passive, which cannot preventDefault the page zoom or scroll.\n viewport.addEventListener(\"wheel\", onWheel, { passive: false });\n return () => viewport.removeEventListener(\"wheel\", onWheel);\n }, [viewportRef, enabled, mode, zoomAt]);\n}\n\nfunction useDragPan(panBy: (dx: number, dy: number) => void): {\n panning: boolean;\n handlers: PanZoomViewportHandlers;\n} {\n const [panning, setPanning] = useState(false);\n const drag = useRef<{ pointerId: number; x: number; y: number; moved: boolean } | null>(null);\n const endDrag = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return;\n drag.current = null;\n setPanning(false);\n };\n return {\n panning,\n handlers: {\n onPointerDown: (event) => {\n if (event.button !== 0) return;\n drag.current = { pointerId: event.pointerId, x: event.clientX, y: event.clientY, moved: false };\n },\n onPointerMove: (event) => {\n const active = drag.current;\n if (!active || active.pointerId !== event.pointerId) return;\n const dx = event.clientX - active.x;\n const dy = event.clientY - active.y;\n if (!active.moved) {\n if (Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n // Capturing only once a drag is under way leaves plain clicks on nodes and edges alone,\n // and retargets the click that ends a drag to the viewport so it selects nothing.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n setPanning(true);\n }\n drag.current = { pointerId: active.pointerId, x: event.clientX, y: event.clientY, moved: true };\n panBy(dx, dy);\n },\n onPointerUp: endDrag,\n onPointerCancel: endDrag,\n },\n };\n}\n\n/**\n * Pan and zoom for a fixed-size content box inside a clipping viewport:\n * ctrl/meta + wheel (which is also what a trackpad pinch sends) — or any wheel, with `wheel: \"zoom\"` — zooms around\n * the pointer, dragging pans, and `zoomIn`/`zoomOut`/`fit` back a control\n * cluster. The content stays fitted to the viewport — across content and\n * viewport size changes — until the user pans or zooms, and again after\n * `fit()` or `reset()`.\n */\nexport function usePanZoom({ enabled, contentWidth, contentHeight, opening, wheel = \"modifier\" }: UsePanZoomOptions): PanZoom {\n const viewportRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [transform, setTransform] = useState<PanZoomTransform>({ x: 0, y: 0, scale: 1 });\n // Which fit the content is held to, until the user pans or zooms.\n const fitted = useRef<\"opening\" | \"everything\" | undefined>(\"opening\");\n const { minScale, focus } = opening ?? {};\n const [focusX, focusY] = [focus?.x, focus?.y];\n\n const refit = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport || !fitted.current) return;\n const content = overflowedSize(contentRef.current, { width: contentWidth, height: contentHeight });\n const fit: FitOptions =\n fitted.current === \"everything\"\n ? {}\n : {\n ...(minScale !== undefined ? { minScale } : {}),\n ...(focusX !== undefined && focusY !== undefined ? { focus: { x: focusX, y: focusY } } : {}),\n };\n setTransform(fitTransform(content, viewportSize(viewport), DEFAULT_LIMITS, fit));\n }, [contentWidth, contentHeight, minScale, focusX, focusY]);\n const zoomAt = useCallback((point: { x: number; y: number }, factor: number) => {\n fitted.current = undefined;\n setTransform((current) => zoomAround(current, point, factor));\n }, []);\n const zoomAtCentre = (factor: number) => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n const { width, height } = viewportSize(viewport);\n zoomAt({ x: width / 2, y: height / 2 }, factor);\n };\n const { panning, handlers } = useDragPan((dx, dy) => {\n fitted.current = undefined;\n setTransform((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));\n });\n useViewportResize(viewportRef, enabled, refit);\n useWheelZoom(viewportRef, enabled, wheel, zoomAt);\n const hold = (fit: \"opening\" | \"everything\") => () => {\n fitted.current = fit;\n refit();\n };\n\n return {\n viewportRef,\n contentRef,\n handlers: enabled ? handlers : {},\n transform,\n panning,\n zoomIn: () => zoomAtCentre(BUTTON_ZOOM_FACTOR),\n zoomOut: () => zoomAtCentre(1 / BUTTON_ZOOM_FACTOR),\n fit: hold(\"everything\"),\n reset: hold(\"opening\"),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -15,9 +15,9 @@ function detectMode() {
|
|
|
15
15
|
}
|
|
16
16
|
function getVersionInfo() {
|
|
17
17
|
return {
|
|
18
|
-
commit: read(() => "
|
|
19
|
-
tag: read(() => "clicky-ui@0.3.
|
|
20
|
-
date: read(() => "2026-10-
|
|
18
|
+
commit: read(() => "9ef931dd", ""),
|
|
19
|
+
tag: read(() => "clicky-ui@0.3.44", ""),
|
|
20
|
+
date: read(() => "2026-10-05T13:41:10.767Z", ""),
|
|
21
21
|
dirty: read(() => true, false),
|
|
22
22
|
mode: detectMode()
|
|
23
23
|
};
|