Mobile UI pass: responsive tables, deck-view drill-down, dialog fixes
- Library/Card Search/Deck tables stack into dense card-style rows below 720px instead of scrolling horizontally (floated thumbnail, wrapping chips for MV/Owned/color pips). - Deck view: fix desktop min-widths and a non-wrapping button row that forced horizontal scroll on phones; split the deck list and the deck-building pane into separate drill-down screens with a back button so a long deck list doesn't bury the builder. - Suggested Cards modal: fix action buttons crushing the card name down to one word per line by giving them their own full-width row. - Add a close (X) button to the shared dialog title bar — mobile has no Escape key and wide dialogs leave little backdrop to tap. - Info tab, bottom-nav loading/safe-area fixes, and Gradle NDK_HOME inheritance fix carried over from the Android bring-up work.
This commit is contained in:
@@ -138,7 +138,9 @@ export const Icons = {
|
||||
/**
|
||||
* A centered modal dialog (backdrop + card) shared by [`ConfirmDialog`],
|
||||
* [`PromptDialog`], and other dialogs built outside this module (e.g. the
|
||||
* command palette). Dismisses on Escape or backdrop click.
|
||||
* command palette). Dismisses on Escape, backdrop click, or the title-bar
|
||||
* close button — the explicit button matters most on mobile, where there's
|
||||
* no Escape key and a `wide` dialog can leave little to no backdrop to tap.
|
||||
*/
|
||||
export function DialogFrame({
|
||||
title,
|
||||
@@ -161,7 +163,10 @@ export function DialogFrame({
|
||||
return (
|
||||
<div className="dialog-backdrop" onClick={onCancel}>
|
||||
<div className={`dialog ${wide ? "dialog-wide" : ""}`} onClick={(e) => e.stopPropagation()}>
|
||||
<h3 className="dialog-title">{title}</h3>
|
||||
<div className="dialog-title-row">
|
||||
<h3 className="dialog-title">{title}</h3>
|
||||
<IconButton icon={Icons.close} label="Close" onClick={onCancel} />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1256,11 +1261,19 @@ export function usePersistedState<T>(key: string, initial: T): [T, (v: T) => voi
|
||||
|
||||
export type ViewMode = "list" | "tiles";
|
||||
|
||||
/** View-mode state persisted to localStorage so it survives tab switches. */
|
||||
/** View-mode state persisted to localStorage so it survives tab switches.
|
||||
*
|
||||
* With nothing persisted yet, the default itself is width-aware: List's wide
|
||||
* multi-column table forces horizontal scrolling on a phone, so narrow
|
||||
* screens start on Tiles (a reflowing grid, no horizontal scroll) instead.
|
||||
* This only picks the *default* — once a mode is explicitly chosen it's
|
||||
* remembered and used everywhere, same as before. */
|
||||
export function useViewMode(key: string): [ViewMode, (m: ViewMode) => void] {
|
||||
const [mode, setMode] = useState<ViewMode>(
|
||||
() => (localStorage.getItem(key) as ViewMode) || "list",
|
||||
);
|
||||
const [mode, setMode] = useState<ViewMode>(() => {
|
||||
const saved = localStorage.getItem(key) as ViewMode | null;
|
||||
if (saved) return saved;
|
||||
return typeof window !== "undefined" && window.innerWidth <= 720 ? "tiles" : "list";
|
||||
});
|
||||
const set = useCallback(
|
||||
(m: ViewMode) => {
|
||||
localStorage.setItem(key, m);
|
||||
|
||||
Reference in New Issue
Block a user