99 </ script >
1010 < style >
1111 * , * ::before , * ::after { box-sizing : border-box; margin : 0 ; }
12- body { font-family : system-ui, -apple-system, sans-serif; padding : 2rem ; background : # fafafa ; color : # 18181b ; }
13- h1 { font-size : 1rem ; font-weight : 600 ; margin-bottom : 1rem ; }
14- .bar { display : flex; align-items : center; gap : 1rem ; margin-bottom : 1rem ; }
15- button {
16- padding : 6px 16px ; border-radius : 6px ; border : 1px solid # e4e4e7 ;
17- background : # 18181b ; color : # fff ; font-size : 13px ; cursor : pointer;
18- }
12+ body { font-family : system-ui, -apple-system, sans-serif; padding : 2rem ; background : # fafafa ; }
13+ h1 { font-size : 1rem ; font-weight : 600 ; margin-bottom : 1rem ; }
14+ .bar { display : flex; align-items : center; gap : 1rem ; margin-bottom : 1rem ; }
15+ button { padding : 6px 16px ; border-radius : 6px ; border : 1px solid # e4e4e7 ;
16+ background : # 18181b ; color : # fff ; font-size : 13px ; cursor : pointer; }
1917 button : disabled { opacity : 0.35 ; cursor : not-allowed; }
2018 # status { font-size : 12px ; font-family : monospace; color : # 71717a ; }
21- iframe {
22- display : block; width : 100% ; height : 480px ;
23- border : 1px solid # e4e4e7 ; border-radius : 10px ;
24- background : # fff ;
25- }
19+ iframe { display : block; width : 100% ; height : 480px ;
20+ border : 1px solid # e4e4e7 ; border-radius : 10px ; background : # fff ; }
2621 </ style >
2722</ head >
2823< body >
2924
3025< h1 > iframe-flight × Vega-Lite — single file, no install</ h1 >
31-
3226< div class ="bar ">
3327 < button id ="btn " disabled > New snapshot</ button >
34- < span id ="status "> Connecting …</ span >
28+ < span id ="status "> Loading …</ span >
3529</ div >
36-
3730< iframe id ="frame "> </ iframe >
3831
3932< script type ="module ">
4033import { ArrowParentEmitter , tableFromArrays , tableToIPC } from 'iframe-flight' ;
4134
42- // ── Child iframe HTML — injected via srcdoc, no separate file needed ──────
35+ // ── Child iframe HTML ─────────────────────────────────────────────── ──────
4336const CHILD = `<!DOCTYPE html>
4437<html><head><meta charset="UTF-8">
4538<script type="importmap">
46- {"imports":{"iframe-flight":"https://ihatexcel.github.io/iframe-flight/iframe-flight.esm.js"}}
47- <\/script>
48- <style>body{margin:0;padding:12px 16px;font-family:system-ui}<\/style>
49- </head><body>
39+ {"imports":{"iframe-flight":"https://ihatexcel.github.io/iframe-flight/iframe-flight.esm.js"}}
40+ <\/script></head>
41+ <body style="margin:0;padding:16px;font-family:system-ui">
5042<div id="c"></div>
5143<script type="module">
5244import { ArrowChildReceiver } from 'iframe-flight';
@@ -65,22 +57,14 @@ <h1>iframe-flight × Vega-Lite — single file, no install</h1>
6557 data: { values: rows },
6658 mark: { type: 'bar', cornerRadiusEnd: 5 },
6759 encoding: {
68- y: { field: 'product', type: 'nominal', sort: '-x', title: null,
69- axis: { labelFontSize: 13 } },
70- x: { field: 'revenue', type: 'quantitative', title: 'Revenue ($M)',
71- axis: { gridColor: '#f4f4f5', labelColor: '#71717a', titleColor: '#71717a' } },
60+ y: { field: 'product', type: 'nominal', sort: '-x', title: null },
61+ x: { field: 'revenue', type: 'quantitative', title: 'Revenue ($M)' },
7262 color: { field: 'growth', type: 'quantitative', title: 'Growth %',
73- scale: { scheme: 'redyellowgreen', domain: [-25, 25] },
74- legend: { orient: 'right', format: '+.0f', labelFontSize: 10, titleFontSize: 10 } },
75- tooltip: [
76- { field: 'product' },
77- { field: 'revenue', title: 'Revenue ($M)', format: '.1f' },
78- { field: 'growth', title: 'Growth %', format: '+.1f' },
79- { field: 'units', title: 'Units', format: ',' }
80- ]
63+ scale: { scheme: 'redyellowgreen', domain: [-25, 25] } },
64+ tooltip: [{ field:'product' }, { field:'revenue', format:'.1f' },
65+ { field:'growth', format:'+.1f' }, { field:'units', format:',' }]
8166 },
82- config: { view: { stroke: null }, background: '#fff',
83- font: 'system-ui, -apple-system, sans-serif' }
67+ config: { view: { stroke: null }, background: '#fff' }
8468 }, { actions: false });
8569});
8670<\/script>
@@ -105,21 +89,20 @@ <h1>iframe-flight × Vega-Lite — single file, no install</h1>
10589const btn = document . getElementById ( 'btn' ) ;
10690const status = document . getElementById ( 'status' ) ;
10791
108- frame . srcdoc = CHILD ;
92+ let emitter = null ;
10993
110- const emitter = new ArrowParentEmitter ( frame , {
111- handshakeTimeout : 20000 ,
112- allowedOrigins : [ '*' ] ,
94+ // Create the emitter only after the iframe document has loaded,
95+ // so contentWindow is stable and CHILD_READY is already in-flight.
96+ frame . addEventListener ( 'load' , ( ) => {
97+ emitter = new ArrowParentEmitter ( frame , { allowedOrigins : [ '*' ] } ) ;
98+ emitter
99+ . onReady ( ( ) => { status . textContent = 'Ready' ; btn . disabled = false ; push ( ) ; } )
100+ . onError ( err => { status . textContent = `❌ ${ err . message } ` ; } ) ;
113101} ) ;
114102
115- emitter
116- . onReady ( ( ) => {
117- btn . disabled = false ;
118- push ( ) ;
119- } )
120- . onError ( err => {
121- status . textContent = `❌ ${ err . message } ` ;
122- } ) ;
103+ btn . addEventListener ( 'click' , push ) ;
104+
105+ frame . srcdoc = CHILD ;
123106
124107async function push ( ) {
125108 btn . disabled = true ;
@@ -128,8 +111,6 @@ <h1>iframe-flight × Vega-Lite — single file, no install</h1>
128111 status . textContent = `${ ack . rows } rows · ${ ack . processingTime } ms` ;
129112 btn . disabled = false ;
130113}
131-
132- btn . addEventListener ( 'click' , push ) ;
133114</ script >
134115
135116</ body >
0 commit comments