From 2107764550b094ffc22c8e8a4e870cf9eb995d26 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 13 Apr 2026 15:01:07 +0200 Subject: [PATCH] fix: cap properties panel label column width --- src/components/DynamicPropertiesPanel.tsx | 93 +++++++++++++++-------- src/components/TypeSelector.tsx | 5 +- src/components/propertyPanelLayout.ts | 5 ++ 3 files changed, 69 insertions(+), 34 deletions(-) create mode 100644 src/components/propertyPanelLayout.ts diff --git a/src/components/DynamicPropertiesPanel.tsx b/src/components/DynamicPropertiesPanel.tsx index 91ae8f1d..0b8c05c3 100644 --- a/src/components/DynamicPropertiesPanel.tsx +++ b/src/components/DynamicPropertiesPanel.tsx @@ -9,6 +9,7 @@ import { TypeSelector } from './TypeSelector' import { AddPropertyForm } from './AddPropertyForm' import type { PropertyDisplayMode } from '../utils/propertyTypes' import { FOCUS_NOTE_ICON_PROPERTY_EVENT } from './noteIconPropertyEvents' +import { PROPERTY_PANEL_COLUMN_STYLE } from './propertyPanelLayout' function toSentenceCase(key: string): string { const spaced = key.replace(/[_-]/g, ' ') @@ -23,6 +24,10 @@ export function containsWikilinks(value: FrontmatterValue): boolean { return false } +const PROPERTY_ROW_CLASS_NAME = 'group/prop grid min-h-7 min-w-0 grid-cols-2 items-center gap-2 rounded px-1.5 outline-none transition-colors hover:bg-muted focus:bg-muted focus:ring-1 focus:ring-primary' +const PROPERTY_LABEL_CLASS_NAME = 'flex max-w-full min-w-0 items-center gap-1 text-[12px] text-muted-foreground' +const SUGGESTED_PROPERTY_SLOT_CLASS_NAME = 'grid min-h-7 min-w-0 grid-cols-2 items-center gap-2 rounded border-none bg-transparent px-1.5 text-left outline-none transition-colors hover:bg-muted focus:bg-muted focus:ring-1 focus:ring-primary cursor-pointer' + function PropertyRow({ propKey, value, editingKey, displayMode, autoMode, vaultStatuses, vaultTags, onStartEdit, onSave, onSaveList, onUpdate, onDelete, onDisplayModeChange }: { propKey: string; value: FrontmatterValue; editingKey: string | null displayMode: PropertyDisplayMode; autoMode: PropertyDisplayMode @@ -40,9 +45,9 @@ function PropertyRow({ propKey, value, editingKey, displayMode, autoMode, vaultS } return ( -
- - {toSentenceCase(propKey)} +
+ + {toSentenceCase(propKey)} {onDelete && ( )} @@ -88,18 +93,63 @@ function getSuggestedDisplayMode(key: string): PropertyDisplayMode { function SuggestedPropertySlot({ label, onAdd }: { label: string; onAdd: () => void }) { return ( ) } +function getExistingPropertyKeys(propertyEntries: [string, FrontmatterValue][], frontmatter: ParsedFrontmatter): Set { + const keys = new Set(propertyEntries.map(([key]) => key.toLowerCase())) + for (const key of Object.keys(frontmatter)) keys.add(key.toLowerCase()) + return keys +} + +function getMissingSuggestedProperties(canAddProperty: boolean, existingKeys: Set, pendingSuggestedKey: string | null) { + if (!canAddProperty) return [] + + return SUGGESTED_PROPERTIES.filter( + ({ key }) => !existingKeys.has(key.toLowerCase()) && key !== pendingSuggestedKey, + ) +} + +function getIconPropertyKey(propertyEntries: [string, FrontmatterValue][]) { + return propertyEntries.find(([key]) => key.toLowerCase() === 'icon')?.[0] +} + +function useFocusNoteIconProperty({ + onAddProperty, + propertyEntries, + setEditingKey, +}: { + onAddProperty?: (key: string, value: FrontmatterValue) => void + propertyEntries: [string, FrontmatterValue][] + setEditingKey: (key: string | null) => void +}) { + useEffect(() => { + const handleFocusNoteIcon = () => { + const existingIconKey = getIconPropertyKey(propertyEntries) + + if (!existingIconKey) { + if (!onAddProperty) return + onAddProperty('icon', '') + } + + setEditingKey(existingIconKey ?? 'icon') + } + + window.addEventListener(FOCUS_NOTE_ICON_PROPERTY_EVENT, handleFocusNoteIcon) + return () => window.removeEventListener(FOCUS_NOTE_ICON_PROPERTY_EVENT, handleFocusNoteIcon) + }, [onAddProperty, propertyEntries, setEditingKey]) +} + export function DynamicPropertiesPanel({ entry, frontmatter, entries, onUpdateProperty, onDeleteProperty, onAddProperty, onNavigate, @@ -120,18 +170,11 @@ export function DynamicPropertiesPanel({ } = usePropertyPanelState({ entries, entryIsA: entry.isA, frontmatter, onUpdateProperty, onDeleteProperty, onAddProperty }) const [pendingSuggestedKey, setPendingSuggestedKey] = useState(null) - const existingKeys = useMemo(() => { - const keys = new Set(propertyEntries.map(([k]) => k.toLowerCase())) - // Also check full frontmatter for relationship keys that are filtered out of propertyEntries - for (const k of Object.keys(frontmatter)) keys.add(k.toLowerCase()) - return keys - }, [propertyEntries, frontmatter]) - - const missingSuggested = onAddProperty - ? SUGGESTED_PROPERTIES.filter( - p => !existingKeys.has(p.key.toLowerCase()) && p.key !== pendingSuggestedKey, - ) - : [] + const existingKeys = useMemo(() => getExistingPropertyKeys(propertyEntries, frontmatter), [propertyEntries, frontmatter]) + const missingSuggested = useMemo( + () => getMissingSuggestedProperties(Boolean(onAddProperty), existingKeys, pendingSuggestedKey), + [existingKeys, onAddProperty, pendingSuggestedKey], + ) const handleSuggestedAdd = useCallback((key: string) => { if (!onAddProperty) return @@ -157,21 +200,7 @@ export function DynamicPropertiesPanel({ onAddProperty(key, trimmed) }, [onAddProperty, setEditingKey]) - useEffect(() => { - const handleFocusNoteIcon = () => { - const existingIconKey = propertyEntries.find(([key]) => key.toLowerCase() === 'icon')?.[0] - - if (!existingIconKey) { - if (!onAddProperty) return - onAddProperty('icon', '') - } - - setEditingKey(existingIconKey ?? 'icon') - } - - window.addEventListener(FOCUS_NOTE_ICON_PROPERTY_EVENT, handleFocusNoteIcon) - return () => window.removeEventListener(FOCUS_NOTE_ICON_PROPERTY_EVENT, handleFocusNoteIcon) - }, [onAddProperty, propertyEntries, setEditingKey]) + useFocusNoteIconProperty({ onAddProperty, propertyEntries, setEditingKey }) return (
diff --git a/src/components/TypeSelector.tsx b/src/components/TypeSelector.tsx index 40bd6459..c28bca1b 100644 --- a/src/components/TypeSelector.tsx +++ b/src/components/TypeSelector.tsx @@ -2,6 +2,7 @@ import type { FrontmatterValue } from './Inspector' import { Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue } from '@/components/ui/select' import { getTypeColor, getTypeLightColor } from '../utils/typeColors' import { getTypeIcon } from './NoteItem' +import { PROPERTY_PANEL_COLUMN_STYLE } from './propertyPanelLayout' const TYPE_NONE = '__none__' @@ -22,7 +23,7 @@ function TypeSelectorItem({ type, typeColorKeys, typeIconKeys }: { function ReadOnlyType({ isA, customColorKey, onNavigate }: { isA?: string | null; customColorKey?: string | null; onNavigate?: (target: string) => void }) { if (!isA) return null return ( -
+
Type
{onNavigate ? ( @@ -57,7 +58,7 @@ export function TypeSelector({ isA, customColorKey, availableTypes, typeColorKey const typeLightColor = isA ? getTypeLightColor(isA, typeColorKeys[isA] ?? customColorKey) : undefined return ( -
+
Type