docs: add import floating window design spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6003300135
commit
5dee76ecd1
@ -0,0 +1,249 @@
|
||||
# Import Floating Window Design
|
||||
|
||||
## Overview
|
||||
|
||||
This design replaces the current full-screen import modal with a **draggable, resizable, minimizable floating window**. The user can freely position the literature selector over the main page, minimizing it when not in use, maximizing it when focusing on selection, and closing it without losing context.
|
||||
|
||||
**What changes:** The import experience shifts from a full-screen modal overlay to an independent floating panel.
|
||||
|
||||
**What stays the same:** Collection tree navigation, item browsing, checkbox multi-selection across collections, abstract preview behavior, and batch import flow all remain identical.
|
||||
|
||||
---
|
||||
|
||||
## Window Attributes
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| Default size | `width: 90vw; height: 75vh` |
|
||||
| Default position | Horizontally and vertically centered |
|
||||
| Minimum size | `min-width: 40rem; min-height: 30rem` |
|
||||
| z-index | `40` (same as current modal) |
|
||||
| Border radius | `16px` (rounded corners to distinguish from full-screen modal) |
|
||||
| Shadow | Enhanced shadow: `0 24px 64px rgba(35, 31, 21, 0.22)` |
|
||||
| Background | `var(--surface)` |
|
||||
|
||||
---
|
||||
|
||||
## Window Structure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ [≡] Import From Zotero 当前项目:xxx [_][□][×] │ ← toolbar (draggable)
|
||||
├──────────────────────┬──────────────────────────────────┤
|
||||
│ Collections │ Collection Items │
|
||||
│ ├─ Collection A (5) │ ☑ Title 1 2023 [i] │
|
||||
│ ├─ Collection B (3) │ ☑ Title 2 2021 [i] │
|
||||
│ └─ Collection C (8) │ ☐ Title 3 2019 [i] │
|
||||
│ │ ... │
|
||||
├──────────────────────┴──────────────────────────────────┤
|
||||
│ 已选 3 篇 [清空选择] [导入所选] │ ← bottom action bar
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Toolbar (Draggable Header)
|
||||
|
||||
- Left: grab handle icon `[≡]` + title "Import From Zotero" + project label
|
||||
- Right: window control buttons `[_]` `[□]` `[×]`
|
||||
- Cursor: `grab` on toolbar, `grabbing` while dragging
|
||||
- Dragging moves the window by delta from pointer movement
|
||||
|
||||
### Window Controls
|
||||
|
||||
- **`_` (Minimize):** Reduces window to a small bar at bottom-right of viewport (`height: 2.5rem; width: 14rem`). The bar shows "导入文献" label + `[□]` button. Clicking the bar restores the window.
|
||||
- **`□` (Maximize):** Expands window to `100vw × 100vh` (same as current modal). Clicking again restores to previous size/position.
|
||||
- **`×` (Close):** Closes the window and clears session state for the window (position/size/isMinimized/isMaximized).
|
||||
|
||||
### Bottom Action Bar
|
||||
|
||||
- Left: selected count ("已选 N 篇")
|
||||
- Right: "清空选择" + "导入所选"
|
||||
- Same behavior as current modal
|
||||
|
||||
### Internal Layout
|
||||
|
||||
- `display: grid; grid-template-columns: 22rem minmax(0, 1fr); min-height: 0;`
|
||||
- Same two-column layout as current modal (collection tree left, items right)
|
||||
|
||||
---
|
||||
|
||||
## Dragging Behavior
|
||||
|
||||
- **Drag handle:** Only the toolbar area is draggable
|
||||
- **Implementation:** `pointerdown` on toolbar → `pointermove` globally to update `left`/`top` → `pointerup` to end
|
||||
- **Boundary constraint:** `left >= 0`, `top >= 0`, `left + width <= viewport.width`, `top + height <= viewport.height`
|
||||
- **Restore on first open:** If session state has no saved position, center the window
|
||||
|
||||
---
|
||||
|
||||
## Minimized State
|
||||
|
||||
- Window becomes a thin bar fixed to bottom-right corner of viewport
|
||||
- Bar contents: "导入文献" text + `[□]` restore button
|
||||
- Bar `z-index: 50` (above main window and main page elements)
|
||||
- Clicking the bar restores window to last saved size/position
|
||||
- When restored, window returns to last non-minimized state (position, size, maximized or not)
|
||||
|
||||
---
|
||||
|
||||
## Maximized State
|
||||
|
||||
- Window expands to fill the entire viewport (`width: 100vw; height: 100vh; left: 0; top: 0`)
|
||||
- Same layout and behavior as the original full-screen modal
|
||||
- The `[□]` button toggles between maximized and restored state
|
||||
|
||||
---
|
||||
|
||||
## Session State
|
||||
|
||||
Key: `zotero-kb.import-window` in `sessionStorage`
|
||||
|
||||
```json
|
||||
{
|
||||
"x": 120,
|
||||
"y": 80,
|
||||
"width": 1100,
|
||||
"height": 700,
|
||||
"isMinimized": false,
|
||||
"isMaximized": false
|
||||
}
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Read on page load; if empty, use default centered position
|
||||
- Write on every state change (move, resize, minimize, maximize, restore)
|
||||
- Clear on browser session end
|
||||
- Window opening behavior: if `isMinimized`, show minimized bar; if `isMaximized`, show maximized; otherwise restore `x, y, width, height`
|
||||
|
||||
---
|
||||
|
||||
## CSS Changes Summary
|
||||
|
||||
### New Classes
|
||||
|
||||
```css
|
||||
.window-frame {
|
||||
position: fixed;
|
||||
z-index: 40;
|
||||
width: var(--fw-width, 90vw);
|
||||
height: var(--fw-height, 75vh);
|
||||
left: var(--fw-left, calc((100vw - var(--fw-width, 90vw)) / 2));
|
||||
top: var(--fw-top, calc((100vh - var(--fw-height, 75vh)) / 2));
|
||||
min-width: 40rem;
|
||||
min-height: 30rem;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 24px 64px rgba(35, 31, 21, 0.22);
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
overflow: hidden;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.window-frame.maximized {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.window-frame.minimized {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.window-minimized-bar {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 50;
|
||||
height: 2.5rem;
|
||||
width: 14rem;
|
||||
border-radius: 8px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
box-shadow: 0 8px 24px rgba(35, 31, 21, 0.14);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0.75rem;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.window-minimized-bar:hover {
|
||||
background: var(--panel);
|
||||
}
|
||||
```
|
||||
|
||||
### Removed / Modified
|
||||
|
||||
- Remove `.modal-shell`, `.modal-overlay`, `.modal-card` full-screen overlay styles
|
||||
- Replace with `.window-frame` for the floating import window
|
||||
|
||||
---
|
||||
|
||||
## State Keys
|
||||
|
||||
| Key | Type | Description |
|
||||
|-----|------|-------------|
|
||||
| `isImportWindowOpen` | boolean | Whether the floating window is open (not minimized) |
|
||||
| `windowX`, `windowY` | number | Pixel position of window top-left |
|
||||
| `windowWidth`, `windowHeight` | number | Pixel dimensions |
|
||||
| `isWindowMinimized` | boolean | Whether window is in minimized bar state |
|
||||
| `isWindowMaximized` | boolean | Whether window is in full-screen state |
|
||||
|
||||
All persisted to `sessionStorage` key `zotero-kb.import-window`.
|
||||
|
||||
---
|
||||
|
||||
## Interaction Changes
|
||||
|
||||
| Action | New Behavior |
|
||||
|--------|-------------|
|
||||
| Click `导入文献` | Open floating window at saved or default position |
|
||||
| Drag toolbar | Move window freely within viewport |
|
||||
| Click `_` | Minimize to bottom-right bar |
|
||||
| Click minimized bar | Restore window |
|
||||
| Click `□` | Toggle maximized (full-screen ↔ restored) |
|
||||
| Click `×` | Close window, clear import window session state |
|
||||
| Press `Esc` | Same as clicking `×` (close) |
|
||||
|
||||
---
|
||||
|
||||
## What Stays The Same
|
||||
|
||||
- Collection tree rendering and expand/collapse
|
||||
- Item list rendering with checkbox, title, year, details button
|
||||
- Cross-collection selection persistence
|
||||
- Abstract hover preview (3s delay)
|
||||
- Selection count, clear, and import actions
|
||||
- Import API call and success handling (refresh cards, close window)
|
||||
- Disabled import button when no project is selected
|
||||
|
||||
---
|
||||
|
||||
## Constraints and Tradeoffs
|
||||
|
||||
### Why Not Actual `window.open`?
|
||||
|
||||
A native browser window would create separate JavaScript context, requiring `postMessage` for state synchronization with the main page. A DOM-based floating panel keeps all state in one JavaScript context and is lighter weight.
|
||||
|
||||
### Why Minimize to Bar, Not Just Hide?
|
||||
|
||||
The user wants the literature selector to be accessible without disrupting the main page. A minimize-to-bar pattern (like VSCode) keeps the entry point visible without cluttering the viewport.
|
||||
|
||||
### Why 90vw × 75vh Default?
|
||||
|
||||
This is large enough to browse a collection tree and item list comfortably, while leaving the main page visible on large monitors — supporting the user's workflow of referencing the main page while selecting.
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Floating window opens at centered 90vw × 75vh on first open
|
||||
- [ ] Window position/size persists across page refreshes in the same session
|
||||
- [ ] Toolbar dragging moves the window freely within viewport bounds
|
||||
- [ ] `_` button minimizes to bottom-right bar; bar click restores
|
||||
- [ ] `□` button maximizes to full-screen and restores to previous size
|
||||
- [ ] `×` button closes and clears window session state
|
||||
- [ ] `Esc` key closes the window
|
||||
- [ ] Main page remains interactive when the floating window is minimized
|
||||
- [ ] All existing import behaviors (collection tree, item selection, import flow) are unchanged
|
||||
Loading…
Reference in New Issue
Block a user