Skip to content

Commit c86ac24

Browse files
committed
Improve mobile editing and hot list readability
1 parent a037cae commit c86ac24

3 files changed

Lines changed: 244 additions & 47 deletions

File tree

src/components/HomePage.tsx

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import {
88
type WeatherForecast,
99
type WeatherRealtime,
1010
} from "../api";
11-
import { Check, PencilLine } from "lucide-react";
11+
import {
12+
ArrowDown,
13+
ArrowUp,
14+
Check,
15+
GripVertical,
16+
PencilLine,
17+
} from "lucide-react";
1218
import {
1319
getHomeCards,
1420
moveHomeCard,
@@ -133,6 +139,9 @@ export function HomePage({
133139
const isInteractiveDragTarget = (target: HTMLElement) =>
134140
Boolean(target.closest("a, button, input, textarea, select, label"));
135141

142+
const isDragHandleTarget = (target: HTMLElement) =>
143+
Boolean(target.closest("[data-home-drag-handle]"));
144+
136145
const handlePointerDown = (
137146
event: PointerEvent<HTMLDivElement>,
138147
cardId: HomeCardId,
@@ -142,10 +151,11 @@ export function HomePage({
142151
const target = event.target as HTMLElement;
143152
if (
144153
event.pointerType === "mouse" ||
145-
isInteractiveDragTarget(target)
154+
!isDragHandleTarget(target)
146155
) {
147156
return;
148157
}
158+
event.preventDefault();
149159
event.currentTarget.setPointerCapture(event.pointerId);
150160
setPointerDrag({
151161
cardId,
@@ -205,7 +215,12 @@ export function HomePage({
205215
) => {
206216
if (!isEditing) return;
207217
const target = event.target as HTMLElement;
208-
if (event.button !== 0 || isInteractiveDragTarget(target)) return;
218+
if (
219+
event.button !== 0 ||
220+
(isInteractiveDragTarget(target) && !isDragHandleTarget(target))
221+
) {
222+
return;
223+
}
209224
const startX = event.clientX;
210225
const startY = event.clientY;
211226
let isActive = false;
@@ -254,6 +269,20 @@ export function HomePage({
254269
window.addEventListener("mouseup", handleMouseUp);
255270
};
256271

272+
const moveCardByStep = (
273+
cardId: HomeCardId,
274+
column: HomeCardColumn,
275+
step: -1 | 1,
276+
) => {
277+
const index = homeCardLayout[column].indexOf(cardId);
278+
const nextIndex = index + step;
279+
if (index < 0 || nextIndex < 0 || nextIndex >= homeCardLayout[column].length) {
280+
return;
281+
}
282+
const insertIndex = step > 0 ? index + 2 : nextIndex;
283+
setHomeCardLayout(moveHomeCard(homeCardLayout, cardId, column, insertIndex));
284+
};
285+
257286
const renderHomeCard = (cardId: HomeCardId) => {
258287
if (cardId === "daily") return <DailyCard state={daily} />;
259288
if (cardId === "hot") {
@@ -316,6 +345,8 @@ export function HomePage({
316345
const isDragging = draggedCard?.cardId === card.id;
317346
const isDropTarget =
318347
dropTarget?.column === column && dropTarget.index === index;
348+
const canMoveUp = index > 0;
349+
const canMoveDown = index >= 0 && index < homeCardLayout[column].length - 1;
319350
return (
320351
<div
321352
className={`home-card-slot ${isDragging ? "is-dragging" : ""} ${
@@ -335,6 +366,37 @@ export function HomePage({
335366
handleMouseDown(event, card.id, column)
336367
}
337368
>
369+
{isEditing && (
370+
<div className="home-card-edit-controls">
371+
<button
372+
type="button"
373+
className="home-card-drag-handle"
374+
aria-label={`拖动${card.label}排序`}
375+
title="拖动排序"
376+
data-home-drag-handle
377+
>
378+
<GripVertical size={16} />
379+
</button>
380+
<button
381+
type="button"
382+
aria-label={`${card.label}上移`}
383+
title="上移"
384+
disabled={!canMoveUp}
385+
onClick={() => moveCardByStep(card.id, column, -1)}
386+
>
387+
<ArrowUp size={15} />
388+
</button>
389+
<button
390+
type="button"
391+
aria-label={`${card.label}下移`}
392+
title="下移"
393+
disabled={!canMoveDown}
394+
onClick={() => moveCardByStep(card.id, column, 1)}
395+
>
396+
<ArrowDown size={15} />
397+
</button>
398+
</div>
399+
)}
338400
{renderHomeCard(card.id)}
339401
</div>
340402
);

src/components/Hot.tsx

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import type { ApiState } from "../types";
66
import { skeletonItems } from "../utils";
77
import { CardTitle, EmptyState, Status } from "./ui";
88

9+
function getHotTitle(item: HotItem, fallback: string) {
10+
return item.title || item.name || item.movie_name || fallback;
11+
}
12+
913
export function HotPage({
1014
apiBase,
1115
}: {
@@ -70,23 +74,27 @@ function HotMiniBoard({
7074
<EmptyState title="暂无热榜数据" desc="上游返回了空列表,稍后刷新即可。" />
7175
) : (
7276
<ol className="rank-list compact-rank">
73-
{displayItems.map((item, index) => (
74-
<li key={`${item.title || item.name || item.movie_name}-${index}`}>
75-
<b>{index + 1}</b>
76-
<a
77-
href={item.link || item.url || "#"}
78-
target="_blank"
79-
rel="noreferrer"
80-
>
81-
{item.title || item.name || item.movie_name || "正在读取..."}
82-
</a>
83-
<span>
84-
{formatHotValue(
85-
item.hot_value ?? item.hot ?? item.heat ?? item.score,
86-
)}
87-
</span>
88-
</li>
89-
))}
77+
{displayItems.map((item, index) => {
78+
const titleText = getHotTitle(item, "正在读取...");
79+
return (
80+
<li key={`${titleText}-${index}`} data-rank-title={titleText}>
81+
<b>{index + 1}</b>
82+
<a
83+
href={item.link || item.url || "#"}
84+
target="_blank"
85+
rel="noreferrer"
86+
title={titleText}
87+
>
88+
{titleText}
89+
</a>
90+
<span>
91+
{formatHotValue(
92+
item.hot_value ?? item.hot ?? item.heat ?? item.score,
93+
)}
94+
</span>
95+
</li>
96+
);
97+
})}
9098
</ol>
9199
)}
92100
</article>
@@ -140,23 +148,27 @@ export function HotBoard({
140148
/>
141149
) : (
142150
<ol className="rank-list">
143-
{displayItems.map((item, index) => (
144-
<li key={`${item.title || item.name || item.movie_name}-${index}`}>
145-
<b>{index + 1}</b>
146-
<a
147-
href={item.link || item.url || "#"}
148-
target="_blank"
149-
rel="noreferrer"
150-
>
151-
{item.title || item.name || item.movie_name || "正在读取热榜..."}
152-
</a>
153-
<span>
154-
{formatHotValue(
155-
item.hot_value ?? item.hot ?? item.heat ?? item.score,
156-
)}
157-
</span>
158-
</li>
159-
))}
151+
{displayItems.map((item, index) => {
152+
const titleText = getHotTitle(item, "正在读取热榜...");
153+
return (
154+
<li key={`${titleText}-${index}`} data-rank-title={titleText}>
155+
<b>{index + 1}</b>
156+
<a
157+
href={item.link || item.url || "#"}
158+
target="_blank"
159+
rel="noreferrer"
160+
title={titleText}
161+
>
162+
{titleText}
163+
</a>
164+
<span>
165+
{formatHotValue(
166+
item.hot_value ?? item.hot ?? item.heat ?? item.score,
167+
)}
168+
</span>
169+
</li>
170+
);
171+
})}
160172
</ol>
161173
)}
162174
</article>

0 commit comments

Comments
 (0)