forked from mengyxu/noob-components
8 changed files with 278 additions and 3 deletions
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
{"file": ".claude/commands/trellis/finish-work.md", "reason": "Finish work checklist"} |
||||
{"file": ".claude/commands/trellis/check-frontend.md", "reason": "Frontend check spec"} |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{"file": ".claude/commands/trellis/check-frontend.md", "reason": "Frontend check spec"} |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
{"file": ".trellis/workflow.md", "reason": "Project workflow and conventions"} |
||||
{"file": ".trellis/spec/frontend/index.md", "reason": "Frontend development guide"} |
||||
{"file": "packages/base/data/list-table-v2.vue", "reason": "Main component being modified"} |
||||
{"file": "packages/base/item/tzDateTime.vue", "reason": "Referenced for timestamp implementation"} |
||||
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
# list-table-v2 Timestamp + Vue JSX |
||||
|
||||
## Goal |
||||
|
||||
Add Vue JSX support to the project and upgrade `list-table-v2.vue` to use `tzDateTime.vue` for timestamp columns with enhanced type support. |
||||
|
||||
## Requirements |
||||
|
||||
### 1. Enable Vue JSX |
||||
- Install `@vitejs/plugin-vue-jsx` package |
||||
- Add JSX plugin to `vite.config.ts` |
||||
|
||||
### 2. Enhanced Timestamp Type |
||||
|
||||
Update `TableColumn.timestamp` type from `boolean` to: |
||||
```typescript |
||||
undefined | boolean | string | { |
||||
valueFormat?: string; |
||||
valueTz?: string; |
||||
displayFormat?: string; |
||||
locale?: string; |
||||
type?: "iso8601" | "unix" | "unixMillis"; |
||||
} |
||||
``` |
||||
|
||||
**Behavior:** |
||||
- `undefined` or falsy → column is not a timestamp |
||||
- `true` → equivalent to `{ type: 'unixMillis', displayFormat: 'YYYY-MM-DD HH:mm:ss' }` |
||||
- `string` → pass to either `type` or `valueFormat` depending on the value |
||||
- Otherwise → pass as props to `TzDateTime` |
||||
|
||||
### 3. Use tzDateTime.vue for Timestamp Columns |
||||
- Import `TzDateTime` component in `list-table-v2.vue` |
||||
- Replace inline `formatStamp` with `TzDateTime` component via JSX rendering |
||||
- Ensure dayjs plugins are loaded when needed |
||||
|
||||
## Technical Notes |
||||
|
||||
### Files to Modify |
||||
1. `vite.config.ts` - Add JSX plugin |
||||
2. `packages/base/data/list-table-v2.vue` - Update timestamp handling |
||||
|
||||
### tzDateTime Props Interface |
||||
```typescript |
||||
interface Props { |
||||
value: string | number; |
||||
valueFormat?: string; |
||||
valueTz?: string; |
||||
displayFormat?: string; |
||||
locale?: string; |
||||
type?: "iso8601" | "unix" | "unixMillis"; |
||||
slot?: boolean; |
||||
} |
||||
``` |
||||
|
||||
### Conversion Logic |
||||
``` |
||||
timestamp value → TzDateTime props: |
||||
- timestamp=true → { type: 'unixMillis', displayFormat: 'YYYY-MM-DD HH:mm:ss' } |
||||
- timestamp='unix' → { type: 'unix' } (shorthand) |
||||
- timestamp='YYYY-MM-DD' → { valueFormat: 'YYYY-MM-DD' } |
||||
- timestamp={ type: 'iso8601' } → passed directly |
||||
``` |
||||
|
||||
## Acceptance Criteria |
||||
- [ ] `@vitejs/plugin-vue-jsx` installed |
||||
- [ ] `vite.config.ts` includes JSX plugin |
||||
- [ ] `TableColumn` interface updated with new timestamp type |
||||
- [ ] `list-table-v2.vue` uses `TzDateTime` for timestamp columns |
||||
- [ ] Backward compatible with existing `timestamp={true}` usage |
||||
- [ ] TypeScript compiles without errors |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
{ |
||||
"id": "list-table-v2-timestamp-jsx", |
||||
"name": "list-table-v2-timestamp-jsx", |
||||
"title": "list-table-v2 timestamp + vue jsx", |
||||
"description": "", |
||||
"status": "planning", |
||||
"dev_type": null, |
||||
"scope": null, |
||||
"priority": "P2", |
||||
"creator": "hechang27-sprt", |
||||
"assignee": "hechang27-sprt", |
||||
"createdAt": "2026-03-25", |
||||
"completedAt": null, |
||||
"branch": null, |
||||
"base_branch": "dev", |
||||
"worktree_path": null, |
||||
"current_phase": 0, |
||||
"next_action": [ |
||||
{ |
||||
"phase": 1, |
||||
"action": "implement" |
||||
}, |
||||
{ |
||||
"phase": 2, |
||||
"action": "check" |
||||
}, |
||||
{ |
||||
"phase": 3, |
||||
"action": "finish" |
||||
}, |
||||
{ |
||||
"phase": 4, |
||||
"action": "create-pr" |
||||
} |
||||
], |
||||
"commit": null, |
||||
"pr_url": null, |
||||
"subtasks": [], |
||||
"children": [], |
||||
"parent": null, |
||||
"relatedFiles": [], |
||||
"notes": "", |
||||
"meta": {} |
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
# Plan: Viewport Resize Handling for list-table-v2 |
||||
|
||||
## 1. Problem Analysis |
||||
|
||||
### Current State |
||||
- The `list-table-v2` component uses a probe row technique to dynamically measure row height when `rowHeight` is NOT explicitly set (dynamic height mode) |
||||
- The `measureProbeRow` function (lines 217-231) already exists and correctly: |
||||
- Clears `estimatedRowHeight.value` before re-measuring |
||||
- Uses TWO `requestAnimationFrame` calls after `nextTick` to let el-table-v2 fully settle |
||||
- Measures the probe row's `offsetHeight` |
||||
|
||||
### Problem |
||||
- When the viewport width changes, column widths may adjust (due to `flexGrow: 1` in `tableColumns` computed) |
||||
- This can cause row heights to change, but currently there is no trigger to re-measure |
||||
- Without re-measuring, the `estimatedRowHeight` may become inaccurate, causing layout issues |
||||
|
||||
### Constraints (from previous attempts) |
||||
1. ResizeObserver on container caused errors in component setup - AVOID |
||||
2. Must use TWO `requestAnimationFrame` calls after `nextTick` for proper el-table-v2 settlement |
||||
3. Must clear `estimatedRowHeight` before re-measuring for el-table-v2 to recalculate properly |
||||
|
||||
--- |
||||
|
||||
## 2. Implementation Approach |
||||
|
||||
### Strategy |
||||
Add a debounced window resize event listener that: |
||||
1. Checks if dynamic height mode is active (`shouldUseProbeRow`) |
||||
2. Calls the existing `measureProbeRow` function |
||||
3. Cleans up properly on component unmount |
||||
|
||||
### Why Window Resize Instead of ResizeObserver |
||||
- `el-auto-resizer` already handles container size changes internally |
||||
- Window resize is the correct trigger because column widths change based on viewport width (via `flexGrow`) |
||||
- ResizeObserver on container caused setup errors in previous attempts |
||||
|
||||
### Debounce Strategy |
||||
- Use lodash's `debounce` (already imported as `lodash-es`) |
||||
- Debounce delay: ~200-300ms to avoid excessive recalculations during active resize |
||||
- The debounced function will be created with `onMounted` and cleaned up with `onUnmounted` |
||||
|
||||
--- |
||||
|
||||
## 3. Code Changes Needed |
||||
|
||||
### File: `/home/hechang27/Documents/sprt/noob-components/packages/base/data/list-table-v2.vue` |
||||
|
||||
#### Import Additions (line 53) |
||||
```typescript |
||||
import { ref, computed, watch, h, onMounted, onUpdated, onUnmounted, useSlots, renderSlot, nextTick } from "vue"; |
||||
``` |
||||
|
||||
#### New State Variable |
||||
After line 66 (`const estimatedRowHeight = ref<number | undefined>(undefined);`): |
||||
```typescript |
||||
// Debounced resize handler for re-measuring probe row |
||||
const debouncedResizeHandler = ref<() => void>(); |
||||
``` |
||||
|
||||
#### New Lifecycle Hooks |
||||
After line 214 (after the existing `watch` block): |
||||
|
||||
```typescript |
||||
// Setup resize listener on mount |
||||
onMounted(() => { |
||||
// Create debounced handler |
||||
debouncedResizeHandler.value = lodash.debounce(() => { |
||||
// Only re-measure in dynamic height mode |
||||
if (shouldUseProbeRow.value) { |
||||
measureProbeRow(); |
||||
} |
||||
}, 250); // 250ms debounce delay |
||||
|
||||
// Attach window resize listener |
||||
window.addEventListener('resize', debouncedResizeHandler.value); |
||||
}); |
||||
|
||||
// Cleanup on unmount |
||||
onUnmounted(() => { |
||||
if (debouncedResizeHandler.value) { |
||||
window.removeEventListener('resize', debouncedResizeHandler.value); |
||||
debouncedResizeHandler.value.cancel(); // Cancel any pending debounce calls |
||||
} |
||||
}); |
||||
``` |
||||
|
||||
--- |
||||
|
||||
## 4. Verification Steps |
||||
|
||||
### Manual Testing |
||||
1. Open a page using `list-table-v2` without explicit `rowHeight` prop |
||||
2. Open browser DevTools and inspect a row element to see its height |
||||
3. Resize the browser window (change width) |
||||
4. Verify that: |
||||
- No console errors occur during resize |
||||
- The row height is recalculated correctly after resize settles |
||||
- The table layout remains correct after resize |
||||
|
||||
### Edge Cases to Verify |
||||
1. **Rapid resize**: Resize window quickly back and forth - should not cause errors or excessive measurements |
||||
2. **Minimum width**: Table should handle minimum width gracefully without breaking |
||||
3. **No explicit rowHeight**: Verify resize handling is NOT active when `rowHeight` IS set (fixed height mode) |
||||
4. **Unmount cleanup**: Navigate away from the page and verify no memory leaks or hanging listeners |
||||
|
||||
### Code Review Checklist |
||||
- [ ] `onUnmounted` is imported and used |
||||
- [ ] Debounced handler is properly cancelled on unmount |
||||
- [ ] `shouldUseProbeRow.value` check prevents unnecessary calls in fixed-height mode |
||||
- [ ] Two RAF frames are used in `measureProbeRow` (existing pattern preserved) |
||||
- [ ] `estimatedRowHeight` is cleared before re-measuring (existing pattern preserved) |
||||
|
||||
--- |
||||
|
||||
## 5. Dependencies |
||||
|
||||
- lodash-es debounce (already imported) |
||||
- Vue lifecycle hooks (already available) |
||||
|
||||
No new dependencies required. |
||||
Loading…
Reference in new issue