Files
goodgo-platform/apps/web/components/design-system/ticker-strip.tsx
Ho Ngoc Hai 9bb4c42f84 feat(web): listings page — ticker-style DataTable với toggle card view
Tạo mới trang /listings dạng bảng ticker-style theo spec TEC-3034.

- DataTable compact (row 36px, sticky header, alternating rows)
- Cột: #, Mã (GG-xxx), Quận, Loại, Giá, Δ30d, DT m², KL/Views
- Sortable theo Giá, Δ30d, DT m², KL/Views
- Filter inline: Loại giao dịch, Loại BĐS, Quận, Khoảng giá
- Toggle view: Table (default) ↔ Card grid (legacy component cũ)
- Pagination restyle compact, giữ nguyên API params
- Click row → navigate to detail page
- Dùng DataTable + PriceDelta từ @/components/design-system

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 01:31:22 +07:00

50 lines
1.5 KiB
TypeScript

import * as React from 'react';
import { cn } from '@/lib/utils';
import { PriceDelta, type PriceDeltaDirection } from './price-delta';
export interface TickerItem {
id: string;
label: string;
changePercent: number;
direction?: PriceDeltaDirection;
}
export interface TickerStripProps extends React.HTMLAttributes<HTMLDivElement> {
items: TickerItem[];
/** Tắt animation (cho unit test / reduced motion). */
paused?: boolean;
}
/**
* Thanh chạy ngang hiển thị biến động giá top quận.
* Render 2 lần liên tiếp để tạo vòng lặp mượt với animation `-50%`.
*/
export function TickerStrip({ items, paused, className, ...rest }: TickerStripProps) {
const duplicated = React.useMemo(() => [...items, ...items], [items]);
return (
<div className={cn('relative h-full overflow-hidden', className)} {...rest}>
<div
className={cn(
'flex h-full w-max items-center gap-6 whitespace-nowrap px-4 font-mono text-ticker',
!paused && 'animate-ticker',
)}
>
{duplicated.map((item, idx) => (
<span
key={`${item.id}-${idx}`}
className="inline-flex items-center gap-2 text-foreground-muted"
>
<span className="text-foreground">{item.label}</span>
<PriceDelta
value={item.changePercent}
size="sm"
hideIcon={false}
direction={item.direction}
/>
</span>
))}
</div>
</div>
);
}