Replaced the large, scrollable IconPicker component with a compact "Button + Dropdown" IconDropdown component, matching the style of the existing payment method dropdown.
Location: src/components/IconDropdown/
Features:
- Compact button showing selected icon (24px-32px)
- Floating dropdown menu (not persistent scrollpane)
- Clean design with [Icon] + [Text Label] layout
- No bulky wrapper boxes around icons
- Matches payment dropdown styling (colors, borders, shadows)
- Click-outside-to-close functionality
- Rotating chevron indicator
- Two size options: 'small' (40px height) and 'medium' (44px height)
Files:
IconDropdown.tsx- Main component with dropdown logicIconDropdown.module.css- Styling matching PaymentCard dropdownindex.ts- Export file
Based on PaymentCard.module.css:
- Button: Rounded (14px), warm beige background (#fff7eb), subtle border
- Dropdown Menu: Floating with shadow, warm background (#fffaf0), max-height 400px
- Menu Items: 48px height, [28px icon] + [label text], hover/active states
- Active State: Darker background, bold font weight
- Icons: 28px in dropdown menu, 24-32px in button (size-dependent)
Before:
<IconPicker selectedIcon={groupIcon} onSelectIcon={setGroupIcon} />After:
<IconDropdown selectedIcon={groupIcon} onSelectIcon={setGroupIcon} size="medium" />Location: Sheet modal → "그룹 추가" form → "아이콘" section
Before:
<div className={styles.iconBtnWrap}>
<button onClick={() => setEmojiOpen((v) => !v)}>
<IconDisplay icon={emoji} size="24px" />
</button>
{emojiOpen && (
<div className={styles.emojiPopover}>
<IconPicker selectedIcon={emoji} onSelectIcon={setEmoji} />
</div>
)}
</div>After:
<div className={styles.iconBtnWrap}>
<IconDropdown selectedIcon={emoji} onSelectIcon={setEmoji} size="small" />
</div>Changes:
- Removed
emojiOpenstate variable - Removed manual button + popover implementation
- IconDropdown handles open/close internally
Location: Settlement form → Title row → Icon button (between title input and camera button)
Before:
<IconPicker selectedIcon={settlementIcon} onSelectIcon={setSettlementIcon} />After:
<IconDropdown selectedIcon={settlementIcon} onSelectIcon={setSettlementIcon} size="medium" />Location: Game setup step → "아이콘" section
AddExpenseButton.tsx:
- Removed:
const [emojiOpen, setEmojiOpen] = useState(false); - Kept:
const [emoji, setEmoji] = useState(DEFAULT_ICON);
Other files already had simple state management, no cleanup needed.
The IconDisplay component from IconPicker.tsx is still used for displaying icons in:
- Group list (SettlementsPage.tsx:206)
- Group header (GroupHeader.tsx:8)
- Settlement list (ExpensesTab.tsx:86)
- Settlement detail (ExpenseDetailPage.tsx:93)
This provides backward compatibility for existing emoji icons in the database.
| Feature | Old IconPicker | New IconDropdown |
|---|---|---|
| Layout | Persistent scrollpane | Toggleable dropdown |
| Height | Always visible, ~400px | Button only, dropdown on demand |
| Icon Size | 40px | 28px in menu, 24-32px in button |
| Wrapper | Box backgrounds | Clean, no boxes |
| Styling | Generic blue theme | Warm beige matching app theme |
| Space Usage | Always occupies vertical space | Minimal when closed |
| User Flow | Scroll through list | Click button → select → auto-close |
- Open: Click button to toggle dropdown
- Close:
- Click outside (handled by
useRef+useEffect) - Select an icon (auto-closes)
- Click button again (toggle)
- Click outside (handled by
aria-expandedattribute on buttonaria-label="아이콘 선택"on buttonrole="listbox"on dropdown menuaria-label="아이콘 선택"on menu
- Dropdown positioned absolutely below button
- Min-width: 200px
- Max-height: 400px with scroll
- Custom scrollbar styling
- Z-index: 40 for proper layering
beers, bowling, bus, cake, drinks, food, friendship, fun, game, heart, home, money, mountain, movie, music, others, reimburse, relax, school, sea, shopping, shylove, taxi, travel, work, working_place
- Group creation with icon dropdown
- Settlement creation with compact icon dropdown
- Settlement edit with icon dropdown
- Game screen settlement setup with icon dropdown
- Click outside to close dropdown
- Auto-close on selection
- Icon display in existing lists (backward compatibility)
- Small vs medium size rendering
- TypeScript compilation
- Before: IconPicker always occupied ~400px vertical space
- After: IconDropdown button is 40-44px, dropdown appears only when needed
- Matches existing payment dropdown design
- Consistent with app's warm color palette
- Professional, polished appearance
- Faster selection (no scrolling through persistent list)
- Less visual clutter in forms
- Clearer visual hierarchy
- Familiar dropdown interaction pattern
- Reuses established dropdown pattern
- Simpler state management in AddExpenseButton
- Consistent component API across all three locations
- Easier to maintain
- Old emoji icons (e.g., "🏖️") still display correctly via
IconDisplay - New selections use icon paths (e.g., "/icons/sea.png")
- No database migration required
- All new groups/settlements default to
/icons/money.png - Previously used various emoji defaults (🏖️, 🍀, 💰)
- ❌ None -
IconPicker.tsxkept forIconDisplayexport
- ✅
IconDropdowncomponent and styles - ✅ Uses existing
ICON_OPTIONSfromconstants/icons.ts