From ea9f374568f3fc4f13f4d46aa88ae9ce62e8de9b Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 17 Feb 2026 22:15:10 +0200 Subject: [PATCH] docs: add custom dropdowns and date pickers design --- ...-17-custom-dropdowns-datepickers-design.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 docs/plans/2026-02-17-custom-dropdowns-datepickers-design.md diff --git a/docs/plans/2026-02-17-custom-dropdowns-datepickers-design.md b/docs/plans/2026-02-17-custom-dropdowns-datepickers-design.md new file mode 100644 index 0000000..f95cd4c --- /dev/null +++ b/docs/plans/2026-02-17-custom-dropdowns-datepickers-design.md @@ -0,0 +1,107 @@ +# Custom Dropdowns & Date Pickers Design + +**Goal:** Replace all native `` elements with custom Vue 3 components that match ZeroClock's dark UI. + +**Architecture:** Two reusable components (`AppSelect`, `AppDatePicker`) using `` for overflow-safe positioning. Drop-in replacements — no store or logic changes needed. + +--- + +## Inventory + +### Native `` (6 instances) +1. **Entries.vue** — Start Date filter +2. **Entries.vue** — End Date filter +3. **Reports.vue** — Start Date +4. **Reports.vue** — End Date +5. **Invoices.vue** — Invoice Date (required) +6. **Invoices.vue** — Due Date (optional) + +--- + +## Component 1: AppSelect + +### Trigger +- Styled like existing inputs: `bg-bg-inset`, `border-border-subtle`, `rounded-xl` +- Shows selected label or placeholder in `text-text-tertiary` +- `ChevronDown` icon (Lucide) on right, rotates 180deg when open +- Disabled state: `opacity-40`, `cursor-not-allowed` + +### Dropdown Panel +- Teleported to ``, positioned via `getBoundingClientRect()` +- `bg-bg-surface`, `border border-border-visible`, `rounded-xl`, shadow +- Max-height with overflow-y scroll +- Options: hover `bg-bg-elevated`, selected item shows `Check` icon in accent color +- Animate in: scale + fade (150ms) + +### API +```vue + +``` + +### Behavior +- Click trigger toggles open/close +- Click option selects and closes +- Click outside closes +- Keyboard: Arrow keys navigate, Enter selects, Escape closes +- Tab moves focus away and closes + +--- + +## Component 2: AppDatePicker + +### Trigger +- Text input showing formatted date ("Feb 17, 2026") or placeholder +- `Calendar` icon (Lucide) on right +- Same input styling as AppSelect trigger + +### Calendar Popover +- Teleported to ``, positioned below trigger +- Header: `ChevronLeft`/`ChevronRight` arrows, center "Month Year" label +- 7-column grid: Mon-Sun header row, day cells +- Prev/next month padding days in `text-text-tertiary` +- Today: ring/border in amber accent +- Selected: solid amber accent background, white text +- Hover: `bg-bg-elevated` +- Click day: selects, closes popover, updates model + +### API +```vue + +``` + +### Behavior +- v-model is `YYYY-MM-DD` string (same format as native date input) +- Click trigger opens calendar +- Click outside closes +- Keyboard: Arrow keys navigate days, Enter selects, Escape closes +- Month navigation via chevron buttons + +--- + +## Integration + +Pure visual swap — replace each native element with the custom component, keep the same `v-model` binding. No store or routing changes. + +**Files created:** `src/components/AppSelect.vue`, `src/components/AppDatePicker.vue` +**Files modified:** Timer.vue, Projects.vue, Entries.vue, Invoices.vue, Reports.vue + +The `datetime-local` input in Entries.vue edit dialog stays as-is (not in scope).