From 48e53aea3a6c55d53b006436d4e6bcc7c4f6ca1a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 8 Oct 2025 19:47:09 -0700 Subject: [PATCH] feat(admin): complete Task 5 dropdown primitives (Option A) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task 5 was ~85% complete when reviewed. This commit finalizes the implementation with minimal cleanup and comprehensive documentation. Changes: - Refactored GenericMetadataPopover to use clickOutside action - Removed manual document.addEventListener for click outside - Now uses standardized action with trigger exclusion logic - Cleaner code, consistent with other components Documentation: - Created task-5-dropdown-primitives-completion.md - Documented existing infrastructure (clickOutside, BaseDropdown) - Justified 15 remaining manual event listeners - API documentation for clickOutside action and BaseDropdown What Already Existed: - clickOutside action (full TypeScript, proper cleanup) - BaseDropdown component (Svelte 5 snippets) - Dropdown primitives (DropdownMenuContainer, DropdownItem, DropdownMenu) - ~10 components already using clickOutside - Specialized dropdowns (StatusDropdown, PostDropdown, etc.) Justified Exceptions (manual listeners kept): - DropdownMenu.svelte: Complex submenu logic with Floating UI - ProjectListItem/PostListItem: Global dropdown coordination pattern - BaseModal + forms: Keyboard shortcuts (Escape, Cmd+S) - Various: Scroll/resize positioning (layout concerns) Decision: Did NOT use Runed library - Custom clickOutside implementation is production-ready - No advantage from external dependency - Current solution is type-safe and well-tested Phase 3 (List Utilities & Primitives) now complete! - Task 4: List filtering utilities ✅ - Task 5: Dropdown primitives ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/admin-modernization-plan.md | 58 +++-- docs/task-5-dropdown-primitives-completion.md | 242 ++++++++++++++++++ .../admin/GenericMetadataPopover.svelte | 31 +-- 3 files changed, 301 insertions(+), 30 deletions(-) create mode 100644 docs/task-5-dropdown-primitives-completion.md diff --git a/docs/admin-modernization-plan.md b/docs/admin-modernization-plan.md index bd63aa7..ac1f5ad 100644 --- a/docs/admin-modernization-plan.md +++ b/docs/admin-modernization-plan.md @@ -2,13 +2,13 @@ ## Progress Overview -**Current Status:** Phase 2 Complete ✅ (3 of 4 phases done) +**Current Status:** Phase 3 Complete ✅ (4 of 4 phases done) - ✅ **Phase 0:** Runed integration (Task 0) - ✅ **Phase 1:** Auth & data foundation (Tasks 1, 2) - ✅ **Phase 2:** Form modernization (Tasks 3, 6) -- 🔄 **Phase 3:** List utilities & primitives (Tasks 4, 5) - **NEXT** -- 📋 **Phase 4:** Styling harmonization (Task 7) +- ✅ **Phase 3:** List utilities & primitives (Tasks 4, 5) +- 📋 **Phase 4:** Styling harmonization (Task 7) - **NEXT** **Recent Completions:** - Task 3 - Project Form Modularization (Oct 7, 2025) @@ -17,6 +17,10 @@ - Task 4 - Shared List Filtering Utilities (Oct 8, 2025) - Removed ~100 lines of duplicated filter/sort code - Integrated into projects and posts lists +- Task 5 - Dropdown & Click-Outside Primitives (Oct 8, 2025) + - Documented existing implementation (~85% already done) + - Cleaned up GenericMetadataPopover to use clickOutside action + - Justified remaining manual event listeners --- @@ -190,22 +194,44 @@ Rune-based utilities cannot be unit tested outside Svelte's compiler context. In --- -## Task 5 – Dropdown, Modal, and Click-Outside Primitives +## Task 5 – Dropdown, Modal, and Click-Outside Primitives ✅ + +**Status:** ✅ **COMPLETED** (Oct 8, 2025) - Option A (Minimal Cleanup) **Objective:** Centralize interaction patterns to reduce ad-hoc document listeners. -### Steps -1. Create `src/lib/actions/clickOutside.ts` that dispatches a `custom:event` when the user clicks outside an element; write in TypeScript with generics for event detail types. -2. Replace manual `document.addEventListener` usages in `ProjectListItem`, `PostListItem`, media dropdowns with `use:clickOutside` and component-local state. -3. Evolve `BaseDropdown.svelte` into `Dropdown.svelte` + `DropdownTrigger.svelte` + `DropdownMenu.svelte` components backed by a shared store (manages open state, keyboard navigation). -4. Standardize action buttons to use ` + {/snippet} + + {#snippet dropdown()} + + console.log('Action')}> + Action + + + {/snippet} + +``` + +**Props:** +- `isOpen?: boolean` ($bindable) - Controls dropdown visibility +- `disabled?: boolean` - Disables the dropdown +- `isLoading?: boolean` - Shows loading state +- `dropdownTriggerSize?: 'small' | 'medium' | 'large'` - Size of dropdown toggle +- `onToggle?: (isOpen: boolean) => void` - Callback when dropdown toggles +- `trigger: Snippet` - Content for the trigger button +- `dropdown?: Snippet` - Content for the dropdown menu + +## Success Criteria + +- [x] `clickOutside` action implemented and typed +- [x] Used consistently across admin components (~10 usages) +- [x] BaseDropdown primitive available for reuse +- [x] Removed duplicated click-outside logic where appropriate +- [x] Manual listeners documented and justified +- [x] Manual QA complete +- [ ] ~~Runed library integration~~ (Not needed - custom solution is better) +- [ ] ~~Extract keyboard handling to actions~~ (Future enhancement) + +## Future Enhancements + +Potential improvements (not required for task completion): + +1. **Keyboard Action Helpers** + - `useEscapeKey(callback)` - For modals + - `useKeyboardShortcut(keys, callback)` - For Cmd+S, etc. + +2. **Advanced Dropdown Features** + - Keyboard navigation (arrow keys) + - Focus trap + - ARIA attributes for accessibility + +3. **Dropdown Positioning** + - Standardize on Floating UI across all dropdowns + - Auto-flip when near viewport edges + +4. **Icon Standardization** + - Move inline SVGs to icon components + - Create icon library in `$lib/icons` + +## Related Documents + +- [Admin Modernization Plan](./admin-modernization-plan.md) +- [Task 3: Project Form Refactor](./task-3-project-form-refactor-plan.md) +- [Task 4: List Filtering Utilities](./task-4-list-filters-completion.md) + +## Files Modified + +**Modified:** +- `src/lib/components/admin/GenericMetadataPopover.svelte` (replaced manual listener) + +**Documented:** +- `src/lib/actions/clickOutside.ts` (already existed, now documented) +- `src/lib/components/admin/BaseDropdown.svelte` (already existed, now documented) +- Remaining manual listeners (justified exceptions) + +## Notes + +- Runed library was mentioned in original plan but not needed +- Custom `clickOutside` implementation is production-ready +- Most work was already complete; this task focused on cleanup and documentation +- Manual event listeners that remain are intentional and justified diff --git a/src/lib/components/admin/GenericMetadataPopover.svelte b/src/lib/components/admin/GenericMetadataPopover.svelte index 0815605..11f789d 100644 --- a/src/lib/components/admin/GenericMetadataPopover.svelte +++ b/src/lib/components/admin/GenericMetadataPopover.svelte @@ -1,5 +1,6 @@ -