fix: iframe resize #224
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
"deploy": "npx wrangler deploy",
|
||||
"cf-typegen": "wrangler types",
|
||||
"build": "pnpm with-env vite build",
|
||||
"build:embed": "vite build --config vite.embed.config.ts",
|
||||
"serve": "vite preview",
|
||||
"test": "vitest run",
|
||||
"format": "biome format",
|
||||
@@ -26,11 +27,13 @@
|
||||
"@faker-js/faker": "^9.6.0",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@hyperdx/node-opentelemetry": "^0.8.1",
|
||||
"@iframe-resizer/child": "^5.0.0",
|
||||
"@iframe-resizer/parent": "^5.0.0",
|
||||
"@number-flow/react": "0.3.5",
|
||||
"@openpanel/common": "workspace:^",
|
||||
"@openpanel/constants": "workspace:^",
|
||||
"@openpanel/integrations": "workspace:^",
|
||||
"@openpanel/importer": "workspace:^",
|
||||
"@openpanel/integrations": "workspace:^",
|
||||
"@openpanel/json": "workspace:*",
|
||||
"@openpanel/payments": "workspace:*",
|
||||
"@openpanel/sdk-info": "workspace:^",
|
||||
|
||||
73
apps/start/public/openpanel-embed.js
Normal file
73
apps/start/public/openpanel-embed.js
Normal file
File diff suppressed because one or more lines are too long
@@ -11,6 +11,7 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as IframeTestRouteImport } from './routes/iframe-test'
|
||||
import { Route as StepsRouteImport } from './routes/_steps'
|
||||
import { Route as PublicRouteImport } from './routes/_public'
|
||||
import { Route as LoginRouteImport } from './routes/_login'
|
||||
@@ -94,6 +95,11 @@ const AppOrganizationIdProjectIdProfilesProfileIdRouteImport = createFileRoute(
|
||||
'/_app/$organizationId/$projectId_/profiles/$profileId',
|
||||
)()
|
||||
|
||||
const IframeTestRoute = IframeTestRouteImport.update({
|
||||
id: '/iframe-test',
|
||||
path: '/iframe-test',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const StepsRoute = StepsRouteImport.update({
|
||||
id: '/_steps',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
@@ -474,6 +480,7 @@ const AppOrganizationIdProjectIdProfilesProfileIdTabsEventsRoute =
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/iframe-test': typeof IframeTestRoute
|
||||
'/$organizationId': typeof AppOrganizationIdRouteWithChildren
|
||||
'/login': typeof LoginLoginRoute
|
||||
'/reset-password': typeof LoginResetPasswordRoute
|
||||
@@ -532,6 +539,7 @@ export interface FileRoutesByFullPath {
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/iframe-test': typeof IframeTestRoute
|
||||
'/login': typeof LoginLoginRoute
|
||||
'/reset-password': typeof LoginResetPasswordRoute
|
||||
'/onboarding': typeof PublicOnboardingRoute
|
||||
@@ -587,6 +595,7 @@ export interface FileRoutesById {
|
||||
'/_login': typeof LoginRouteWithChildren
|
||||
'/_public': typeof PublicRouteWithChildren
|
||||
'/_steps': typeof StepsRouteWithChildren
|
||||
'/iframe-test': typeof IframeTestRoute
|
||||
'/_app/$organizationId': typeof AppOrganizationIdRouteWithChildren
|
||||
'/_login/login': typeof LoginLoginRoute
|
||||
'/_login/reset-password': typeof LoginResetPasswordRoute
|
||||
@@ -654,6 +663,7 @@ export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/iframe-test'
|
||||
| '/$organizationId'
|
||||
| '/login'
|
||||
| '/reset-password'
|
||||
@@ -712,6 +722,7 @@ export interface FileRouteTypes {
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| '/iframe-test'
|
||||
| '/login'
|
||||
| '/reset-password'
|
||||
| '/onboarding'
|
||||
@@ -766,6 +777,7 @@ export interface FileRouteTypes {
|
||||
| '/_login'
|
||||
| '/_public'
|
||||
| '/_steps'
|
||||
| '/iframe-test'
|
||||
| '/_app/$organizationId'
|
||||
| '/_login/login'
|
||||
| '/_login/reset-password'
|
||||
@@ -836,6 +848,7 @@ export interface RootRouteChildren {
|
||||
LoginRoute: typeof LoginRouteWithChildren
|
||||
PublicRoute: typeof PublicRouteWithChildren
|
||||
StepsRoute: typeof StepsRouteWithChildren
|
||||
IframeTestRoute: typeof IframeTestRoute
|
||||
ApiConfigRoute: typeof ApiConfigRoute
|
||||
ApiHealthcheckRoute: typeof ApiHealthcheckRoute
|
||||
ShareOverviewShareIdRoute: typeof ShareOverviewShareIdRoute
|
||||
@@ -843,6 +856,13 @@ export interface RootRouteChildren {
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/iframe-test': {
|
||||
id: '/iframe-test'
|
||||
path: '/iframe-test'
|
||||
fullPath: '/iframe-test'
|
||||
preLoaderRoute: typeof IframeTestRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/_steps': {
|
||||
id: '/_steps'
|
||||
path: ''
|
||||
@@ -1694,6 +1714,7 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
LoginRoute: LoginRouteWithChildren,
|
||||
PublicRoute: PublicRouteWithChildren,
|
||||
StepsRoute: StepsRouteWithChildren,
|
||||
IframeTestRoute: IframeTestRoute,
|
||||
ApiConfigRoute: ApiConfigRoute,
|
||||
ApiHealthcheckRoute: ApiHealthcheckRoute,
|
||||
ShareOverviewShareIdRoute: ShareOverviewShareIdRoute,
|
||||
|
||||
28
apps/start/src/routes/iframe-test.tsx
Normal file
28
apps/start/src/routes/iframe-test.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ScriptOnce, createFileRoute } from '@tanstack/react-router';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export const Route = createFileRoute('/iframe-test')({
|
||||
component: IframeTestLayout,
|
||||
});
|
||||
|
||||
function IframeTestLayout() {
|
||||
return (
|
||||
<div className="w-full h-full center-center p-32">
|
||||
<div className="border-8 border-border rounded-lg p-4 w-full max-w-5xl">
|
||||
<iframe
|
||||
data-openpanel-embed
|
||||
src="http://localhost:3000/share/overview/zef2XC"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
scrolling="no"
|
||||
loading="lazy"
|
||||
title="OpenPanel Dashboard"
|
||||
/>
|
||||
</div>
|
||||
<script src="/openpanel-embed.js" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -12,14 +12,37 @@ import OverviewTopPages from '@/components/overview/overview-top-pages';
|
||||
import OverviewTopSources from '@/components/overview/overview-top-sources';
|
||||
import { useTRPC } from '@/integrations/trpc/react';
|
||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { createFileRoute, notFound, useSearch } from '@tanstack/react-router';
|
||||
import {
|
||||
ScriptOnce,
|
||||
createFileRoute,
|
||||
notFound,
|
||||
useSearch,
|
||||
} from '@tanstack/react-router';
|
||||
import { EyeClosedIcon, FrownIcon } from 'lucide-react';
|
||||
import { z } from 'zod';
|
||||
import '@iframe-resizer/child';
|
||||
|
||||
const shareSearchSchema = z.object({
|
||||
header: z.optional(z.number().or(z.string().or(z.boolean()))),
|
||||
});
|
||||
|
||||
const iframeResizerScript = `
|
||||
(function() {
|
||||
if (typeof window !== 'undefined' && window.iFrameResizer) {
|
||||
window.iFrameResizer.onMessage = function(message) {
|
||||
if (message && message.type === 'load-custom-styles') {
|
||||
var css = (message.opts && message.opts.styles) || '';
|
||||
if (!css) return;
|
||||
var style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.appendChild(document.createTextNode(css));
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
||||
`;
|
||||
|
||||
export const Route = createFileRoute('/share/overview/$shareId')({
|
||||
component: RouteComponent,
|
||||
validateSearch: shareSearchSchema,
|
||||
@@ -76,7 +99,8 @@ function RouteComponent() {
|
||||
header !== '0' && header !== 0 && header !== 'false' && header !== false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ minHeight: '100vh' }}>
|
||||
<ScriptOnce>{iframeResizerScript}</ScriptOnce>
|
||||
{isHeaderVisible && (
|
||||
<div className="mx-auto max-w-7xl justify-between row gap-4 p-4 pb-0">
|
||||
<div className="col gap-1">
|
||||
|
||||
32
apps/start/src/scripts/openpanel-embed.ts
Normal file
32
apps/start/src/scripts/openpanel-embed.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import iframeResize from '@iframe-resizer/parent';
|
||||
|
||||
(() => {
|
||||
function initOpenPanelEmbeds() {
|
||||
iframeResize(
|
||||
{
|
||||
license: 'GPLv3', // OpenPanel is AGPL-3.0, compatible with GPL-3.0
|
||||
checkOrigin: true,
|
||||
log: true, // Enable logging for testing
|
||||
onReady(iframe) {
|
||||
console.log('iframeResizer ready', iframe);
|
||||
const styles = iframe.getAttribute('data-openpanel-styles');
|
||||
if (styles) {
|
||||
console.log('sending message to load custom styles');
|
||||
console.log('styles', styles);
|
||||
iframe.iFrameResizer.sendMessage({
|
||||
type: 'load-custom-styles',
|
||||
opts: { styles },
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'iframe[data-openpanel-embed]',
|
||||
);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initOpenPanelEmbeds);
|
||||
} else {
|
||||
initOpenPanelEmbeds();
|
||||
}
|
||||
})();
|
||||
17
apps/start/vite.embed.config.ts
Normal file
17
apps/start/vite.embed.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: './src/scripts/openpanel-embed.ts',
|
||||
output: {
|
||||
format: 'iife',
|
||||
dir: 'public',
|
||||
entryFileNames: 'openpanel-embed.js',
|
||||
name: 'OpenPanelEmbed',
|
||||
},
|
||||
},
|
||||
minify: true,
|
||||
emptyOutDir: false,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user