the tab panels share one grid cell, so nothing jumps
Making Typefaces one row equalised the two tabs by luck, not by construction — the labels and the hints under the selects still differed, and any tab that grows a row would put the jump straight back. So every panel now sits in the SAME grid cell. The block is as tall as the tallest of them and stops moving entirely, and it stays right when a tab is added or one of them grows. Measuring instead would have meant a ResizeObserver, a re-measure after every edit, and a wrong answer for one frame after each. `col-start-1 row-start-1` gets the same number out of the layout engine for nothing. `invisible`, not `hidden`. An invisible element still takes its space, which IS the mechanism — and it is out of the tab order, so the controls behind the front panel cannot be reached by keyboard. The Tabs root is controlled now, because visibility is decided here rather than by Radix unmounting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { readableError } from '../errors';
|
||||
import { useFlash } from '../useFlash';
|
||||
@@ -114,6 +114,8 @@ export const LayoutPanel = () => {
|
||||
const [draft, setDraft] = useState<Layout | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const devPort = useDevPort();
|
||||
/** Controlled, because the grid stack below decides visibility rather than Radix unmounting. */
|
||||
const [tab, setTab] = useState('colours');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [saved, flashSaved] = useFlash();
|
||||
|
||||
@@ -233,7 +235,7 @@ export const LayoutPanel = () => {
|
||||
enough that it does not need capping, which is the other thing this bought.
|
||||
*/}
|
||||
{mode === 'edit' && (
|
||||
<Tabs defaultValue="colours" className="shrink-0 gap-2">
|
||||
<Tabs value={tab} onValueChange={setTab} className="shrink-0 gap-2">
|
||||
<TabsList className="h-7">
|
||||
<TabsTrigger value="colours" className="text-xs">
|
||||
Colours
|
||||
@@ -243,7 +245,24 @@ export const LayoutPanel = () => {
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="colours" className="rounded-lg border border-border p-4">
|
||||
{/*
|
||||
EVERY panel in the same grid cell, so the block is as tall as the TALLEST of them and stops
|
||||
moving when the tab changes.
|
||||
|
||||
Measuring the panels and setting a height would need a ResizeObserver, a re-measure on every
|
||||
edit, and a wrong answer for one frame after each. `grid-area: 1/1` gets it from the layout
|
||||
engine for nothing, and it stays right when a tab is added or one of them grows a row —
|
||||
which is the case that made the first fix insufficient.
|
||||
|
||||
`invisible` and not `hidden`: an invisible element still takes its space, which IS the
|
||||
mechanism. It is also removed from the tab order, so the controls behind the front panel
|
||||
cannot be reached by keyboard.
|
||||
*/}
|
||||
<div className="grid rounded-lg border border-border p-4">
|
||||
<div
|
||||
className={`col-start-1 row-start-1 ${tab === 'colours' ? '' : 'invisible'}`}
|
||||
aria-hidden={tab !== 'colours'}
|
||||
>
|
||||
<>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{COLOURS.map(({ key, label, fallback }) => (
|
||||
@@ -274,9 +293,12 @@ export const LayoutPanel = () => {
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
</TabsContent>
|
||||
</div>
|
||||
|
||||
<TabsContent value="typefaces" className="rounded-lg border border-border p-4">
|
||||
<div
|
||||
className={`col-start-1 row-start-1 ${tab === 'typefaces' ? '' : 'invisible'}`}
|
||||
aria-hidden={tab !== 'typefaces'}
|
||||
>
|
||||
<>
|
||||
{/*
|
||||
Four across, one row.
|
||||
@@ -317,7 +339,8 @@ export const LayoutPanel = () => {
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
</TabsContent>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user