add resize to content hook
This commit is contained in:
parent
f6c8b2a299
commit
2e52837bdb
16
README.md
16
README.md
@ -120,6 +120,22 @@ createUI('dashboard', require.resolve('./Dashboard.tsx')); // CommonJS
|
||||
- **`useProps(defaults)`** — Get props passed from the server
|
||||
- **`sendPrompt(message)`** — Send a message to the AI chat
|
||||
- **`callTool(name, params)`** — Invoke an MCP tool
|
||||
- **`resizeToContent(element?)`** — Request parent to resize iframe to fit content
|
||||
- **`useResizeToContent()`** — Hook that auto-notifies parent when content size changes
|
||||
|
||||
```tsx
|
||||
import { useResizeToContent } from 'mcp-ui-kit/ui';
|
||||
|
||||
function MyComponent() {
|
||||
const containerRef = useResizeToContent();
|
||||
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
{/* Content that may change size */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
BIN
nanobot.db
BIN
nanobot.db
Binary file not shown.
@ -9,8 +9,10 @@
|
||||
"scripts": {
|
||||
"dev": "npm run start --workspace=demo-server & npm run dev --workspace=@mcp-ui-kit/inspector",
|
||||
"start": "npm run start --workspace=demo-server",
|
||||
"build": "npm run build --workspace=packages/library",
|
||||
"start:inspector": "npm run dev --workspace=@mcp-ui-kit/inspector",
|
||||
"publish:library": "npm publish --workspace=packages/library"
|
||||
"publish:library": "npm publish --workspace=packages/library",
|
||||
"nanobot": "export $(cat .env | xargs) && nanobot run ./nanobot.yaml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.6.3"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { generateMockStockData } from './stock-utils';
|
||||
import { callTool, sendPrompt, useProps } from 'mcp-ui-kit/ui';
|
||||
import { callTool, sendPrompt, useProps, useResizeToContent } from 'mcp-ui-kit/ui';
|
||||
|
||||
|
||||
// Types for props passed from the tool handler
|
||||
@ -31,6 +31,9 @@ export function StockDashboard() {
|
||||
timeframe: '1M',
|
||||
});
|
||||
|
||||
// Auto-notify parent when content size changes
|
||||
const containerRef = useResizeToContent();
|
||||
|
||||
const stocks: StockData[] = generateMockStockData(props.symbols);
|
||||
|
||||
const [selectedStock, setSelectedStock] = useState<string>(stocks[0]?.symbol || '');
|
||||
@ -70,7 +73,7 @@ Please analyze this portfolio and provide recommendations.`;
|
||||
return (
|
||||
<>
|
||||
<style>{styles}</style>
|
||||
<div className="dashboard">
|
||||
<div className="dashboard" ref={containerRef}>
|
||||
{/* Header */}
|
||||
<div className="header">
|
||||
<div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { styles } from './styles';
|
||||
import { useResizeToContent } from 'mcp-ui-kit/ui';
|
||||
|
||||
interface WeatherData {
|
||||
temp: number;
|
||||
@ -35,6 +36,7 @@ const weatherDataByCity: WeatherData = {
|
||||
|
||||
|
||||
export function WeatherDashboard() {
|
||||
const containerRef = useResizeToContent();
|
||||
const data = weatherDataByCity;
|
||||
const currentDate = new Date().toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
@ -60,7 +62,7 @@ ${data.forecast.map(day => `${day.day}: ${day.icon} ${day.temp}°C`).join('\n')}
|
||||
return (
|
||||
<>
|
||||
<style>{styles}</style>
|
||||
<div className="container">
|
||||
<div className="container" ref={containerRef}>
|
||||
<div className="card">
|
||||
<div className="header">
|
||||
<h1>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useEffect, useRef, type RefObject } from 'react';
|
||||
|
||||
/**
|
||||
* Send a prompt to the parent window
|
||||
@ -24,6 +25,64 @@ export function callTool(toolName: string, params: Record<string, unknown> = {})
|
||||
}, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the parent to resize the iframe to fit content
|
||||
* Call this after your content has rendered
|
||||
*/
|
||||
export function resizeToContent(element?: HTMLElement) {
|
||||
const target = element || document.body;
|
||||
|
||||
// Use scrollHeight/scrollWidth for full content size (not just visible area)
|
||||
// Also account for any margin on the element
|
||||
const style = window.getComputedStyle(target);
|
||||
const marginTop = parseFloat(style.marginTop) || 0;
|
||||
const marginBottom = parseFloat(style.marginBottom) || 0;
|
||||
const marginLeft = parseFloat(style.marginLeft) || 0;
|
||||
const marginRight = parseFloat(style.marginRight) || 0;
|
||||
|
||||
// For body, also account for body padding/margin
|
||||
let extraHeight = 0;
|
||||
let extraWidth = 0;
|
||||
if (target !== document.body) {
|
||||
const bodyStyle = window.getComputedStyle(document.body);
|
||||
extraHeight = (parseFloat(bodyStyle.paddingTop) || 0) + (parseFloat(bodyStyle.paddingBottom) || 0);
|
||||
extraWidth = (parseFloat(bodyStyle.paddingLeft) || 0) + (parseFloat(bodyStyle.paddingRight) || 0);
|
||||
}
|
||||
|
||||
const width = Math.ceil(target.scrollWidth + marginLeft + marginRight + extraWidth);
|
||||
const height = Math.ceil(target.scrollHeight + marginTop + marginBottom + extraHeight);
|
||||
|
||||
window.parent.postMessage({
|
||||
type: 'resize',
|
||||
payload: { width, height }
|
||||
}, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook that automatically notifies parent when content size changes
|
||||
* Returns a ref to attach to your container element
|
||||
*/
|
||||
export function useResizeToContent<T extends HTMLElement = HTMLDivElement>(): RefObject<T | null> {
|
||||
const ref = useRef<T | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) return;
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
resizeToContent(element);
|
||||
});
|
||||
|
||||
observer.observe(element);
|
||||
// Initial resize
|
||||
resizeToContent(element);
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use MCP props
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user