the device toggle is three labelled tabs
Icons alone told three things apart by proportion at 12 pixels. Full width, one of three filled, and the word from DEVICES. The `size` prop is gone with the bar that needed the small one, and so is the second copy inside Spacing — the panel header scopes the whole editor now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+22
-18
@@ -2,7 +2,7 @@ import { Monitor, Smartphone, Tablet } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
import { useSearchParams } from 'react-router';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import type { DeviceName } from './ViewMode';
|
||||
import { DEVICES, type DeviceName } from './ViewMode';
|
||||
|
||||
/**
|
||||
* Which width everything is showing, and the control for changing it.
|
||||
@@ -44,35 +44,39 @@ const OPTIONS: { value: DeviceName; Icon: typeof Monitor }[] = [
|
||||
];
|
||||
|
||||
/**
|
||||
* The control itself, for an editor section that has per-view settings.
|
||||
* The control itself: three tabs across the width of whatever holds it.
|
||||
*
|
||||
* `size` because the one in the panel's bar is chrome and these are part of a form: the bar's is as
|
||||
* small as a bar allows, and a section's sits above its own fields and should read as belonging to
|
||||
* them.
|
||||
* ── One shape, not two ──
|
||||
*
|
||||
* There was a `size` prop. `bar` was a small icon-only cluster for the panel bar across the top of the
|
||||
* detail panel, and that bar is gone — so what is left is the one shape, and a prop with a single
|
||||
* value is a decision nobody makes.
|
||||
*
|
||||
* ── Labelled, and spread ──
|
||||
*
|
||||
* The icons alone were a rebus: a rectangle, a slightly narrower rectangle, and a narrower one still,
|
||||
* telling three things apart by proportion at 12 pixels. They read as tabs now because they behave
|
||||
* like tabs — one of three, the whole width, the chosen one filled — and the word is what makes the
|
||||
* choice legible without hovering for a tooltip.
|
||||
*
|
||||
* The label comes from DEVICES, which is also what the preview names in its own caption. One list.
|
||||
*/
|
||||
export const DeviceToggle = ({ size = 'bar' }: { size?: 'bar' | 'section' }) => {
|
||||
export const DeviceToggle = () => {
|
||||
const [device, setDevice] = useDevice();
|
||||
const compact = size === 'bar';
|
||||
|
||||
return (
|
||||
<div
|
||||
// In a section it spans the width and spreads its three: it scopes everything below it, so it
|
||||
// reads as a header for the group rather than a control sitting in a corner of one.
|
||||
className={`flex items-center rounded-md border border-border p-0.5 ${
|
||||
compact ? 'gap-0.5' : 'w-full justify-around'
|
||||
}`}
|
||||
>
|
||||
<div className="flex w-full items-center rounded-md border border-border p-0.5">
|
||||
{OPTIONS.map(({ value, Icon }) => (
|
||||
<Button
|
||||
key={value}
|
||||
size="sm"
|
||||
variant={device === value ? 'secondary' : 'ghost'}
|
||||
className={compact ? 'h-5 gap-1 px-2 text-xs' : 'h-7 flex-1 text-xs'}
|
||||
className="h-7 flex-1 gap-1.5 text-xs"
|
||||
onClick={() => setDevice(value)}
|
||||
aria-label={value}
|
||||
title={value}
|
||||
aria-label={DEVICES[value].label}
|
||||
>
|
||||
<Icon className="size-3" />
|
||||
<Icon className="size-3.5" />
|
||||
{DEVICES[value].label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -32,8 +32,8 @@ export const EditorPanel = () => {
|
||||
return (
|
||||
<div className="flex size-full flex-col overflow-hidden">
|
||||
{/* Outside the scroll area, so a long editor cannot carry it off the screen. */}
|
||||
<div className="flex shrink-0 justify-center border-b border-border px-3 py-2">
|
||||
<DeviceToggle size="bar" />
|
||||
<div className="shrink-0 border-b border-border px-3 py-2">
|
||||
<DeviceToggle />
|
||||
</div>
|
||||
|
||||
{editor ? (
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useAutosave } from '../useAutosave';
|
||||
import { usePreviewable, useViewMode } from '../ViewMode';
|
||||
import { applySpacing, paintMeasure, type SpacingDraft } from '../canvas';
|
||||
import { SitePreview, useDevPort } from '../SitePreview';
|
||||
import { DeviceToggle } from '../DeviceToggle';
|
||||
import { useEditorSlot } from '../useEditorSlot';
|
||||
import { type LayoutBand } from './PaddingTab';
|
||||
import { SpacingTab } from './SpacingTab';
|
||||
@@ -348,10 +347,6 @@ export const LayoutPanel = () => {
|
||||
<AccordionItem value="padding" className="rounded-lg border border-border px-4">
|
||||
<AccordionTrigger className="text-xs">Spacing</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{/* The width these settings are for. Reads `?device=`, so it moves the preview too. */}
|
||||
<div className="mb-3">
|
||||
<DeviceToggle size="section" />
|
||||
</div>
|
||||
{/* Three bands. PaddingTab is still the per-SECTION control, for the pages. */}
|
||||
<SpacingTab
|
||||
spacing={draft.spacing}
|
||||
|
||||
Reference in New Issue
Block a user