{isLoading && filteredWorkers.length === 0 ? (
@@ -176,14 +189,14 @@ export function Workbench() {
) : filteredWorkers.length === 0 ? (
) : (
-
+
{filteredWorkers.map((worker) => (
{
columnRefs.current[worker.id] = el;
}}
- className="flex h-full flex-shrink-0"
+ className="flex h-full w-full shrink-0 snap-center md:w-auto md:snap-none"
>
@@ -191,6 +204,33 @@ export function Workbench() {
)}
+
+ {isMobile && filteredWorkers.length > 0 && (
+
+ )}
+
+ {isMobile && (
+
+
+
+ )}
);
}
diff --git a/interface/src/styles.css b/interface/src/styles.css
index 35aa72c65..9fa6d762c 100644
--- a/interface/src/styles.css
+++ b/interface/src/styles.css
@@ -42,6 +42,18 @@
}
}
+/* iOS Safari auto-zooms on focus when input font-size < 16px. Force the
+ computed font-size to 16px on small viewports without changing visual size
+ on desktop. Visual size on mobile is the trade-off: tiny inputs (text-xs /
+ text-tiny) read slightly larger on phones, which is desirable anyway. */
+@media (max-width: 767px) {
+ input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
+ textarea,
+ select {
+ font-size: 16px;
+ }
+}
+
/* Base styles */
html,
body {
diff --git a/interface/src/ui/Drawer.tsx b/interface/src/ui/Drawer.tsx
new file mode 100644
index 000000000..6fe50d1c2
--- /dev/null
+++ b/interface/src/ui/Drawer.tsx
@@ -0,0 +1,96 @@
+import * as RDialog from "@radix-ui/react-dialog";
+import clsx from "clsx";
+import {motion, AnimatePresence} from "framer-motion";
+import type {ReactNode} from "react";
+
+export type DrawerSide = "left" | "right" | "bottom";
+
+interface DrawerProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ side?: DrawerSide;
+ children: ReactNode;
+ className?: string;
+ ariaLabel?: string;
+}
+
+const sideAnim: Record<
+ DrawerSide,
+ {
+ initial: object;
+ animate: object;
+ exit: object;
+ positionClass: string;
+ }
+> = {
+ left: {
+ initial: {x: "-100%"},
+ animate: {x: 0},
+ exit: {x: "-100%"},
+ positionClass: "inset-y-0 left-0 h-full w-[85vw] max-w-[320px]",
+ },
+ right: {
+ initial: {x: "100%"},
+ animate: {x: 0},
+ exit: {x: "100%"},
+ positionClass: "inset-y-0 right-0 h-full w-[85vw] max-w-[320px]",
+ },
+ bottom: {
+ initial: {y: "100%"},
+ animate: {y: 0},
+ exit: {y: "100%"},
+ positionClass: "inset-x-0 bottom-0 max-h-[85vh] w-full",
+ },
+};
+
+export function Drawer({
+ open,
+ onOpenChange,
+ side = "left",
+ children,
+ className,
+ ariaLabel,
+}: DrawerProps) {
+ const cfg = sideAnim[side];
+
+ return (
+
+
+ {open && (
+
+
+
+
+
+
+
+ {ariaLabel || "Drawer"}
+
+ {children}
+
+
+
+ )}
+
+
+ );
+}