Skip to content

Commit 5206914

Browse files
committed
Improve mobile viewport and menu layout
1 parent 4503c47 commit 5206914

5 files changed

Lines changed: 89 additions & 5 deletions

File tree

client/src/app/AppShell.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ const STREAM_TRANSPORT_VALUES = new Set<StreamTransport>([
141141
"h264",
142142
"webrtc",
143143
]);
144+
const MOBILE_VIEWPORT_MEDIA_QUERY = "(max-width: 600px)";
144145
clearLegacyVolatileUiState();
145146

146147
interface StreamQualityResponse {
@@ -240,6 +241,16 @@ function defaultStreamConfigForTransport(
240241
return base;
241242
}
242243

244+
function shouldForceInitialFitMode(): boolean {
245+
if (typeof window === "undefined") {
246+
return false;
247+
}
248+
return (
249+
window.matchMedia?.(MOBILE_VIEWPORT_MEDIA_QUERY).matches ??
250+
window.innerWidth <= 600
251+
);
252+
}
253+
243254
function writeStreamTransportQueryParam(transport: StreamTransport) {
244255
const url = new URL(window.location.href);
245256
if (transport === "auto") {
@@ -344,8 +355,11 @@ export function AppShell({
344355
readDeviceQueryParam() ??
345356
initialUiState.selectedUDID,
346357
);
358+
const forceInitialFitMode = shouldForceInitialFitMode();
347359
const initialViewportState = initialSelectedUDID
348-
? viewportStateForUDID(initialUiState, initialSelectedUDID)
360+
? viewportStateForUDID(initialUiState, initialSelectedUDID, {
361+
forceFit: forceInitialFitMode,
362+
})
349363
: DEFAULT_VIEWPORT_STATE;
350364
const {
351365
error: listError,
@@ -975,6 +989,7 @@ export function AppShell({
975989
const nextViewportState = viewportStateForUDID(
976990
persistedState,
977991
selectedSimulator.udid,
992+
{ forceFit: shouldForceInitialFitMode() },
978993
);
979994
setStreamStamp(Date.now());
980995
setChromeProfile(null);

client/src/app/uiState.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,30 @@ describe("uiState", () => {
104104
expect(viewportStateForUDID({}, "missing")).toEqual(DEFAULT_VIEWPORT_STATE);
105105
});
106106

107+
it("can force a persisted viewport back to fit mode", () => {
108+
expect(
109+
viewportStateForUDID(
110+
{
111+
viewportByUDID: {
112+
"sim-1": {
113+
pan: { x: 20, y: 30 },
114+
rotationQuarterTurns: 1,
115+
viewMode: "manual",
116+
zoom: 1.7,
117+
},
118+
},
119+
},
120+
"sim-1",
121+
{ forceFit: true },
122+
),
123+
).toEqual({
124+
pan: DEFAULT_VIEWPORT_STATE.pan,
125+
rotationQuarterTurns: 1,
126+
viewMode: "fit",
127+
zoom: null,
128+
});
129+
});
130+
107131
it("uses the supplied stored-flag default outside the browser", () => {
108132
expect(readStoredFlag("missing-flag", true)).toBe(true);
109133
});

client/src/app/uiState.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,18 @@ export function writePersistedUiState(
182182
export function viewportStateForUDID(
183183
state: PersistedUiState,
184184
udid: string,
185+
options: { forceFit?: boolean } = {},
185186
): PersistedViewportState {
186-
return state.viewportByUDID?.[udid] ?? DEFAULT_VIEWPORT_STATE;
187+
const viewportState = state.viewportByUDID?.[udid] ?? DEFAULT_VIEWPORT_STATE;
188+
if (!options.forceFit) {
189+
return viewportState;
190+
}
191+
return {
192+
...viewportState,
193+
pan: DEFAULT_VIEWPORT_STATE.pan,
194+
viewMode: "fit",
195+
zoom: null,
196+
};
187197
}
188198

189199
export function sanitizePersistedUiState(

client/src/styles/components.css

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
max-height: min(72vh, 560px);
261261
display: flex;
262262
flex-direction: column;
263+
min-height: 0;
263264
background: var(--surface);
264265
border: 1px solid var(--border);
265266
border-radius: 8px;
@@ -289,9 +290,11 @@
289290
}
290291

291292
.sim-list {
292-
flex: 1;
293+
flex: 1 1 260px;
294+
min-height: 0;
293295
overflow-y: auto;
294296
padding: 4px;
297+
overscroll-behavior: contain;
295298
scrollbar-width: thin;
296299
scrollbar-color: var(--border) transparent;
297300
}
@@ -316,12 +319,14 @@
316319

317320
.menu-actions {
318321
display: flex;
322+
flex: 0 0 auto;
319323
flex-direction: column;
320324
padding: 4px;
321325
}
322326

323327
.menu-section {
324328
display: flex;
329+
flex: 0 0 auto;
325330
flex-direction: column;
326331
gap: 6px;
327332
padding: 8px;
@@ -460,6 +465,7 @@
460465
flex-direction: column;
461466
gap: 2px;
462467
width: 100%;
468+
min-width: 0;
463469
padding: 7px 10px;
464470
border: none;
465471
border-radius: 6px;
@@ -491,18 +497,26 @@
491497
}
492498

493499
.sim-item-name {
500+
min-width: 0;
501+
overflow: hidden;
494502
font-size: 12px;
495503
font-weight: 600;
496504
line-height: 1.3;
505+
text-overflow: ellipsis;
506+
white-space: nowrap;
497507
}
498508

499509
.sim-item-meta {
500510
display: flex;
501511
align-items: center;
502512
gap: 6px;
513+
min-width: 0;
514+
overflow: hidden;
503515
font-size: 11px;
504516
color: var(--text-secondary);
505517
line-height: 1.3;
518+
text-overflow: ellipsis;
519+
white-space: nowrap;
506520
}
507521

508522
.list-empty {

client/src/styles/layout.css

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,29 @@
169169
max-width: calc(100vw - 24px);
170170
}
171171

172-
.menu-popover {
173-
width: min(260px, calc(100vw - 16px));
172+
.toolbar .menu-wrap .menu-popover {
173+
position: fixed;
174+
top: 46px;
175+
right: 6px;
176+
left: 6px;
177+
width: auto;
178+
height: calc(100dvh - 52px);
179+
max-height: none;
180+
overflow-y: auto;
181+
overscroll-behavior: contain;
182+
}
183+
184+
.menu-popover .sidebar-search {
185+
position: sticky;
186+
top: 0;
187+
z-index: 1;
188+
background: var(--surface);
189+
}
190+
191+
.menu-popover .sim-list {
192+
flex: 1 1 55dvh;
193+
min-height: min(360px, 44dvh);
194+
overflow-y: auto;
174195
}
175196

176197
.hierarchy-panel {

0 commit comments

Comments
 (0)