22/**
33 * Delivery Player Layout Component
44 *
5- * Extended layout with mode/role/session/model controls for delivery view .
5+ * Delivery layout wrapper for the element player .
66 */
77import { onMount } from ' svelte' ;
8- import { page } from ' $app/stores' ;
9- import ModeSelector from ' ./ModeSelector.svelte' ;
10- import SessionPanel from ' ./SessionPanel.svelte' ;
11- import ScoringPanel from ' ./ScoringPanel.svelte' ;
12- import ModelPanel from ' ./ModelPanel.svelte' ;
138import { loadController } from ' ../lib/demo-element-loader' ;
149import type { PieController } from ' ../lib/types' ;
1510import type { PlayerType } from ' $lib/config/player-runtime' ;
@@ -44,44 +39,15 @@ let {
4439// State
4540let loading = $state (true );
4641let error = $state <string | null >(null );
47- let score = $state <any >(null );
4842let controllerWarning = $state <string | null >(null );
49- let roleLocked = $state (false );
50- let splitRatio = $state (50 );
5143
5244// Effects
5345$effect (() => {
54- roleLocked = mode === ' evaluate' ;
5546 if (playerRole !== ' instructor' && mode === ' evaluate' ) {
5647 mode = ' view' ;
5748 }
5849});
5950
60- // Call controller in evaluate mode
61- $effect (() => {
62- if (mode === ' evaluate' && controller && model && session ) {
63- if (debug ) console .log (' [delivery-player-layout] Calling controller.score()' );
64-
65- const scoreMethod = controller .score || controller .outcome ;
66-
67- if (scoreMethod ) {
68- scoreMethod (model , session , { mode , role: playerRole , partialScoring })
69- .then ((result : any ) => {
70- score = result ;
71- if (debug ) console .log (' [delivery-player-layout] Score result:' , result );
72- })
73- .catch ((err : any ) => {
74- console .error (' [delivery-player-layout] Scoring error:' , err );
75- if (debug ) score = { error: err .message };
76- });
77- } else {
78- console .warn (' [delivery-player-layout] Controller has no score or outcome method' );
79- }
80- } else {
81- score = null ;
82- }
83- });
84-
8551onMount (async () => {
8652 try {
8753 if (! elementName ) {
@@ -113,47 +79,10 @@ onMount(async () => {
11379 }
11480});
11581
116- function handleSplitPointerDown(event : PointerEvent ) {
117- const container = (event .currentTarget as HTMLElement )?.parentElement ;
118- if (! container ) return ;
119-
120- event .preventDefault ();
121- const target = event .currentTarget as HTMLElement ;
122- target .setPointerCapture (event .pointerId );
123-
124- const startX = event .clientX ;
125- const startRatio = splitRatio ;
126- const rect = container .getBoundingClientRect ();
127-
128- const onMove = (moveEvent : PointerEvent ) => {
129- const delta = moveEvent .clientX - startX ;
130- const next = ((startRatio / 100 ) * rect .width + delta ) / rect .width ;
131- splitRatio = Math .min (80 , Math .max (20 , Math .round (next * 100 )));
132- };
133-
134- const onUp = () => {
135- target .releasePointerCapture (event .pointerId );
136- document .body .style .cursor = ' ' ;
137- window .removeEventListener (' pointermove' , onMove );
138- window .removeEventListener (' pointerup' , onUp );
139- };
140-
141- document .body .style .cursor = ' col-resize' ;
142- window .addEventListener (' pointermove' , onMove );
143- window .addEventListener (' pointerup' , onUp );
144- }
145-
14682function handleModelApply(nextModel : any ) {
14783 // Model updates are handled by the store in parent routes
14884 console .log (' [delivery-player-layout] Model apply requested:' , nextModel );
14985}
150-
151- // Build URL for role change, preserving other params
152- function getRoleUrl(newRole : ' student' | ' instructor' ): string {
153- const url = new URL ($page .url );
154- url .searchParams .set (' role' , newRole );
155- return url .pathname + url .search ;
156- }
15786 </script >
15887
15988<div class =" layout-container" >
@@ -182,56 +111,10 @@ function getRoleUrl(newRole: 'student' | 'instructor'): string {
182111 </div >
183112 {/if }
184113
185- <div class ="player-content" style ={ ` grid-template-columns: ${ splitRatio }% 12px ${ 100 - splitRatio }% ` } >
186- <main class =" min-w-0 pr-3 overflow-auto" >
114+ <div class =" player-content" >
115+ <main class =" flex-1 min-w-0 overflow-auto" >
187116 {@render children ?.()}
188117 </main >
189-
190- <div class ="divider divider-horizontal cursor-col-resize" onpointerdown ={handleSplitPointerDown } role =" separator" aria-orientation =" vertical" ></div >
191-
192- <aside class =" min-w-0 pl-3 overflow-auto space-y-4" >
193- <div class =" card bg-base-100 border border-base-300" >
194- <div class =" card-body p-4" >
195- <h3 class =" card-title text-sm uppercase text-base-content/60" >Mode</h3 >
196- <ModeSelector bind:mode evaluateDisabled ={playerRole !== ' instructor' } />
197- </div >
198- </div >
199-
200- <div class =" card bg-base-100 border border-base-300" >
201- <div class =" card-body p-4" >
202- <h3 class =" card-title text-sm uppercase text-base-content/60" >Role</h3 >
203- <div class =" flex flex-col gap-2" >
204- <a
205- href ={getRoleUrl (' student' )}
206- data-sveltekit-reload
207- class =" btn btn-sm justify-start"
208- class:btn-primary ={playerRole === ' student' }
209- class:btn-outline ={playerRole !== ' student' }
210- class:btn-disabled ={roleLocked }
211- aria-disabled ={roleLocked }
212- tabindex ={roleLocked ? - 1 : 0 }
213- data-testid =" role-student"
214- >
215- Student
216- </a >
217- <a
218- href ={getRoleUrl (' instructor' )}
219- data-sveltekit-reload
220- class =" btn btn-sm justify-start"
221- class:btn-primary ={playerRole === ' instructor' }
222- class:btn-outline ={playerRole !== ' instructor' }
223- data-testid =" role-instructor"
224- >
225- Instructor
226- </a >
227- </div >
228- </div >
229- </div >
230-
231- <SessionPanel {session } />
232-
233- <ScoringPanel {score } />
234- </aside >
235118 </div >
236119 {/if }
237120</div >
@@ -245,25 +128,9 @@ function getRoleUrl(newRole: 'student' | 'instructor'): string {
245128 }
246129
247130 .player-content {
248- display : grid ;
131+ display : flex ;
249132 flex : 1 ;
250133 min-height : 0 ;
251134 overflow : hidden ;
252135 }
253-
254- .player-content > main ,
255- .player-content > aside {
256- min-height : 0 ; /* Allow grid children to shrink below content size */
257- }
258-
259- /* Responsive */
260- @media (max-width : 900px ) {
261- .player-content {
262- grid-template-columns : 1fr !important ;
263- }
264-
265- .divider-horizontal {
266- display : none ;
267- }
268- }
269136 </style >
0 commit comments