diff --git a/apps/web/src/components/a4-preview.tsx b/apps/web/src/components/a4-preview.tsx index 6cfd595..7ee52f7 100644 --- a/apps/web/src/components/a4-preview.tsx +++ b/apps/web/src/components/a4-preview.tsx @@ -1,15 +1,13 @@ import { useLayoutEffect, useRef, useState } from 'react' -import { SheetControls } from '@/components/sheet-controls' import { SheetInspector } from '@/components/sheet-inspector' import { SheetPage } from '@/components/sheet-page' -import { Button } from '@/components/ui/button' import { captionFontStack } from '@/lib/fonts' import { paperSize, sheetLayout } from '@/lib/layout' -import { downloadSheetPdf } from '@/lib/pdf' import { usePhotoStore } from '@/stores/photo-store' import { useSettingsStore } from '@/stores/settings-store' +/** The centre column: the print sheet itself, with a floating frame inspector. */ export function A4Preview() { const photos = usePhotoStore((state) => state.photos) const captionFontId = useSettingsStore((state) => state.captionFontId) @@ -23,24 +21,6 @@ export function A4Preview() { const containerRef = useRef(null) const [width, setWidth] = useState(0) - const [isExporting, setIsExporting] = useState(false) - - const handleExport = async () => { - setIsExporting(true) - try { - await downloadSheetPdf( - photos, - perRow, - showCutMarks, - showCaptions, - showCameraLine, - paper, - captionFontId, - ) - } finally { - setIsExporting(false) - } - } useLayoutEffect(() => { const el = containerRef.current @@ -62,21 +42,14 @@ export function A4Preview() { return (
-
-

- Print sheet ({paper.label}) - {pageCount > 1 ? ` — ${pageCount} pages` : ''} -

- +

+ Print sheet ({paper.label}) + {pageCount > 1 ? ` — ${pageCount} pages` : ''} +

+ {/* Floats over the page so the selected frame's tools follow you. */} +
+
- -
state.captionLocation) - const setCaptionLocation = useSettingsStore( - (state) => state.setCaptionLocation, - ) - const dateFormat = useSettingsStore((state) => state.dateFormat) - const setDateFormat = useSettingsStore((state) => state.setDateFormat) - const showCameraLine = useSettingsStore((state) => state.showCameraLine) - const setShowCameraLine = useSettingsStore((state) => state.setShowCameraLine) - const showCaptions = useSettingsStore((state) => state.showCaptions) - const setShowCaptions = useSettingsStore((state) => state.setShowCaptions) - const applyLocationMode = usePhotoStore((state) => state.applyLocationMode) - const applyDateFormat = usePhotoStore((state) => state.applyDateFormat) - - const selectLocation = (location: CaptionLocation) => { - setCaptionLocation(location) - applyLocationMode(location) - } - - const selectDateFormat = (format: DateFormat) => { - setDateFormat(format) - applyDateFormat(format) - } - - return ( -
-
- - -
-
- - -
- - -
- ) -} diff --git a/apps/web/src/components/caption-font-control.tsx b/apps/web/src/components/caption-font-control.tsx deleted file mode 100644 index 8ef360c..0000000 --- a/apps/web/src/components/caption-font-control.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { CAPTION_FONTS } from '@/lib/fonts' -import { useSettingsStore } from '@/stores/settings-store' - -export function CaptionFontControl() { - const captionFontId = useSettingsStore((state) => state.captionFontId) - const setCaptionFont = useSettingsStore((state) => state.setCaptionFont) - - return ( -
- - -
- ) -} diff --git a/apps/web/src/components/options-panel.tsx b/apps/web/src/components/options-panel.tsx new file mode 100644 index 0000000..a12eb29 --- /dev/null +++ b/apps/web/src/components/options-panel.tsx @@ -0,0 +1,230 @@ +import { type ReactNode, useState } from 'react' + +import { Button } from '@/components/ui/button' +import { DATE_FORMATS, type DateFormat } from '@/lib/date' +import { CAPTION_FONTS } from '@/lib/fonts' +import { LOCATION_DETAILS } from '@/lib/geocode' +import { PAPER_SIZES, paperSize } from '@/lib/layout' +import { downloadSheetPdf } from '@/lib/pdf' +import { usePhotoStore } from '@/stores/photo-store' +import { + type CaptionLocation, + MAX_PER_ROW, + MIN_PER_ROW, + useSettingsStore, +} from '@/stores/settings-store' + +const SELECT_CLASS = + 'border-input bg-background rounded-md border px-2 py-1 text-sm' + +/** The right-hand rail: every caption + sheet option, plus export. */ +export function OptionsPanel() { + const photos = usePhotoStore((state) => state.photos) + const applyLocationMode = usePhotoStore((state) => state.applyLocationMode) + const applyDateFormat = usePhotoStore((state) => state.applyDateFormat) + + const captionLocation = useSettingsStore((state) => state.captionLocation) + const setCaptionLocation = useSettingsStore((state) => state.setCaptionLocation) + const dateFormat = useSettingsStore((state) => state.dateFormat) + const setDateFormat = useSettingsStore((state) => state.setDateFormat) + const captionFontId = useSettingsStore((state) => state.captionFontId) + const setCaptionFont = useSettingsStore((state) => state.setCaptionFont) + const showCameraLine = useSettingsStore((state) => state.showCameraLine) + const setShowCameraLine = useSettingsStore((state) => state.setShowCameraLine) + const showCaptions = useSettingsStore((state) => state.showCaptions) + const setShowCaptions = useSettingsStore((state) => state.setShowCaptions) + const paperSizeId = useSettingsStore((state) => state.paperSizeId) + const setPaperSize = useSettingsStore((state) => state.setPaperSize) + const perRow = useSettingsStore((state) => state.polaroidsPerRow) + const setPerRow = useSettingsStore((state) => state.setPolaroidsPerRow) + const showCutMarks = useSettingsStore((state) => state.showCutMarks) + const setShowCutMarks = useSettingsStore((state) => state.setShowCutMarks) + + const [isExporting, setIsExporting] = useState(false) + const paper = paperSize(paperSizeId) + + const selectLocation = (value: CaptionLocation) => { + setCaptionLocation(value) + applyLocationMode(value) + } + const selectDate = (value: DateFormat) => { + setDateFormat(value) + applyDateFormat(value) + } + + const handleExport = async () => { + setIsExporting(true) + try { + await downloadSheetPdf( + photos, + perRow, + showCutMarks, + showCaptions, + showCameraLine, + paper, + captionFontId, + ) + } finally { + setIsExporting(false) + } + } + + return ( +
+
+ + + + + + + + + + + +
+ +
+ + + + + + setPerRow(Number(event.target.value))} + className="accent-primary w-24" + /> + + {perRow} + + + + +
+ + +
+ ) +} + +function Section({ title, children }: { title: string; children: ReactNode }) { + return ( +
+

+ {title} +

+ {children} +
+ ) +} + +function Field({ + label, + htmlFor, + children, +}: { + label: string + htmlFor: string + children: ReactNode +}) { + return ( +
+ + {children} +
+ ) +} + +function Toggle({ + label, + checked, + onChange, +}: { + label: string + checked: boolean + onChange: (value: boolean) => void +}) { + return ( + + ) +} diff --git a/apps/web/src/components/settings-toolbar.tsx b/apps/web/src/components/settings-toolbar.tsx deleted file mode 100644 index 898ab13..0000000 --- a/apps/web/src/components/settings-toolbar.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { CaptionControls } from '@/components/caption-controls' -import { CaptionFontControl } from '@/components/caption-font-control' - -/** Global caption settings that apply to every photo on the sheet. */ -export function SettingsToolbar() { - return ( -
- - -
- ) -} diff --git a/apps/web/src/components/sheet-controls.tsx b/apps/web/src/components/sheet-controls.tsx deleted file mode 100644 index 6cd61d9..0000000 --- a/apps/web/src/components/sheet-controls.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { PAPER_SIZES } from '@/lib/layout' -import { - MAX_PER_ROW, - MIN_PER_ROW, - useSettingsStore, -} from '@/stores/settings-store' - -export function SheetControls() { - const paperSizeId = useSettingsStore((state) => state.paperSizeId) - const setPaperSize = useSettingsStore((state) => state.setPaperSize) - const perRow = useSettingsStore((state) => state.polaroidsPerRow) - const setPerRow = useSettingsStore((state) => state.setPolaroidsPerRow) - const showCutMarks = useSettingsStore((state) => state.showCutMarks) - const setShowCutMarks = useSettingsStore((state) => state.setShowCutMarks) - - return ( -
-
- - -
-
- - setPerRow(Number(event.target.value))} - className="accent-primary w-24" - /> - - {perRow} - -
- -
- ) -} diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index f2b54fe..661f66d 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -1,10 +1,10 @@ import { createFileRoute } from '@tanstack/react-router' import { A4Preview } from '@/components/a4-preview' +import { OptionsPanel } from '@/components/options-panel' import { PhotoDropzone } from '@/components/photo-dropzone' import { PhotoStrip } from '@/components/photo-strip' import { ProjectControls } from '@/components/project-controls' -import { SettingsToolbar } from '@/components/settings-toolbar' import { usePhotoStore } from '@/stores/photo-store' export const Route = createFileRoute('/')({ @@ -15,28 +15,29 @@ function Home() { const hasPhotos = usePhotoStore((state) => state.photos.length > 0) return ( -
+

Polaroid

- Build polaroid-style print sheets — edit each frame right on the - page. Everything stays on your device. + Build polaroid-style print sheets — edit each frame on the page. + Everything stays on your device.

- {hasPhotos && } - -
+
-
+
+
)