Replace the UI with the Organic design handoff #124

Merged
steve merged 4 commits from feat/organic-ui into main 2026-08-22 23:40:23 +00:00
2 changed files with 22 additions and 27 deletions
Showing only changes of commit 5a9ae58a2a - Show all commits
+20 -26
View File
@@ -49,36 +49,30 @@ export function PlantCard({
return ( return (
<div <div
role="button" className={cn('panel relative transition-shadow hover:[box-shadow:var(--shadow-md)]', open && 'border-accent-400')}
tabIndex={0}
aria-expanded={open}
onClick={() => setOpen((v) => !v)}
onKeyDown={(e) => {
if (e.target === e.currentTarget && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault()
setOpen((v) => !v)
}
}}
className={cn(
'panel relative cursor-pointer px-[18px] py-4 transition-shadow hover:[box-shadow:var(--shadow-md)]',
open && 'border-accent-400',
)}
> >
<div className="flex items-center gap-3"> {/* The whole face is the toggle; the expanded area below has its own controls. */}
<Monogram color={plant.color} letters={letters} /> <button
<span className="flex min-w-0 flex-col gap-px"> type="button"
<span className="truncate font-heading text-[16.5px]" title={plant.name}> aria-expanded={open}
{plant.name} onClick={() => setOpen((v) => !v)}
className="block w-full cursor-pointer rounded-[inherit] px-[18px] py-4 text-left"
>
<div className="flex items-center gap-3">
<Monogram color={plant.color} letters={letters} />
<span className="flex min-w-0 flex-col gap-px">
<span className="truncate font-heading text-[16.5px]" title={plant.name}>
{plant.name}
</span>
<span className="text-xs font-semibold text-ink-mute">{sub.join(' · ')}</span>
</span> </span>
<span className="text-xs font-semibold text-ink-mute">{sub.join(' · ')}</span> {builtin && <Tag tone="neutral" className="ml-auto flex-none">built-in</Tag>}
</span> </div>
{builtin && <Tag tone="neutral" className="ml-auto flex-none">built-in</Tag>} <div className="mt-2.5 text-[12.5px] text-ink-soft">{lotText}</div>
</div> </button>
<div className="mt-2.5 text-[12.5px] text-ink-soft">{lotText}</div>
{open && ( {open && (
// Stop clicks inside the expanded area from toggling the card. <div className="-mt-1.5 flex flex-col gap-2 px-[18px] pb-4">
<div className="mt-2.5 flex flex-col gap-2" onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>
{lots.map((lot) => ( {lots.map((lot) => (
<LotCard key={lot.id} lot={lot} onEdit={() => onEditLot(lot)} onDelete={() => onDeleteLot(lot)} /> <LotCard key={lot.id} lot={lot} onEdit={() => onEditLot(lot)} onDelete={() => onDeleteLot(lot)} />
))} ))}
+2 -1
View File
@@ -101,7 +101,8 @@ export const useEditorStore = create<EditorState>((set) => ({
...TRANSIENT, ...TRANSIENT,
setSel: (sel) => set({ sel }), setSel: (sel) => set({ sel }),
setFocus: (id) => set({ focusId: id }), setFocus: (id) => set({ focusId: id }),
setArmedKind: (kind) => set({ armedKind: kind, ghost: kind ? undefined : null } as Partial<EditorState>), // Disarming drops the ghost too; arming keeps whatever the cursor last set.
setArmedKind: (kind) => set((s) => ({ armedKind: kind, ghost: kind ? s.ghost : null })),
setArmedPlant: (plant, lotId = null) => set({ armedPlant: plant, armedLotId: plant ? lotId : null }), setArmedPlant: (plant, lotId = null) => set({ armedPlant: plant, armedLotId: plant ? lotId : null }),
setArmedLotId: (lotId) => set({ armedLotId: lotId }), setArmedLotId: (lotId) => set({ armedLotId: lotId }),
setGhost: (ghost) => set({ ghost }), setGhost: (ghost) => set({ ghost }),