fix divider css
This commit is contained in:
@@ -19,13 +19,9 @@ interface FileItemProps {
|
|||||||
onView?: () => void;
|
onView?: () => void;
|
||||||
onDownload?: () => void;
|
onDownload?: () => void;
|
||||||
isDownloading?: boolean;
|
isDownloading?: boolean;
|
||||||
showDivider?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = createStyles(({ token, css }) => ({
|
const useStyles = createStyles(({ token, css }) => ({
|
||||||
wrapper: css`
|
|
||||||
width: 100%;
|
|
||||||
`,
|
|
||||||
item: css`
|
item: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -34,16 +30,12 @@ const useStyles = createStyles(({ token, css }) => ({
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.15s ease;
|
transition: background-color 0.15s ease;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${token.colorBgTextHover};
|
background-color: ${token.colorBgTextHover};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
divider: css`
|
|
||||||
height: 1px;
|
|
||||||
background-color: ${token.colorBorder};
|
|
||||||
margin: 0 ${token.paddingMD}px;
|
|
||||||
`,
|
|
||||||
fileInfo: css`
|
fileInfo: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -75,7 +67,7 @@ const formatFileSize = (size: number | undefined): string => {
|
|||||||
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FileItem: React.FC<FileItemProps> = ({ entry, onDelete, onRestore, onView, onDownload, isDownloading, showDivider = true }) => {
|
const FileItem: React.FC<FileItemProps> = ({ entry, onDelete, onRestore, onView, onDownload, isDownloading }) => {
|
||||||
const { t } = useTranslation(["app", "common"]);
|
const { t } = useTranslation(["app", "common"]);
|
||||||
const { styles, cx } = useStyles();
|
const { styles, cx } = useStyles();
|
||||||
const token = useTheme();
|
const token = useTheme();
|
||||||
@@ -103,8 +95,7 @@ const FileItem: React.FC<FileItemProps> = ({ entry, onDelete, onRestore, onView,
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.item} onClick={handleItemClick} data-file-item="true">
|
||||||
<div className={styles.item} onClick={handleItemClick}>
|
|
||||||
<div className={styles.fileInfo}>
|
<div className={styles.fileInfo}>
|
||||||
<div onClick={handleDragHandleClick}>
|
<div onClick={handleDragHandleClick}>
|
||||||
<SortableList.DragHandle />
|
<SortableList.DragHandle />
|
||||||
@@ -116,9 +107,7 @@ const FileItem: React.FC<FileItemProps> = ({ entry, onDelete, onRestore, onView,
|
|||||||
>
|
>
|
||||||
{entry.fileName}
|
{entry.fileName}
|
||||||
</span>
|
</span>
|
||||||
{entry.status === "added" && (
|
{entry.status === "added" && <Badge color={token.colorSuccess} text={t("statusAdded")} style={{ fontSize: token.fontSizeSM, whiteSpace: "nowrap" }} />}
|
||||||
<Badge color={token.colorSuccess} text={t("statusAdded")} style={{ fontSize: token.fontSizeSM, whiteSpace: "nowrap" }} />
|
|
||||||
)}
|
|
||||||
{entry.status === "deleted" && (
|
{entry.status === "deleted" && (
|
||||||
<Badge color={token.colorError} text={t("statusDeleted")} style={{ fontSize: token.fontSizeSM, whiteSpace: "nowrap" }} />
|
<Badge color={token.colorError} text={t("statusDeleted")} style={{ fontSize: token.fontSizeSM, whiteSpace: "nowrap" }} />
|
||||||
)}
|
)}
|
||||||
@@ -148,8 +137,6 @@ const FileItem: React.FC<FileItemProps> = ({ entry, onDelete, onRestore, onView,
|
|||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
{showDivider && <div className={styles.divider} />}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -287,10 +287,9 @@ const FileSection: React.FC<FileSectionProps> = ({
|
|||||||
|
|
||||||
// ── Render item ────────────────────────────────────────────────────────────
|
// ── Render item ────────────────────────────────────────────────────────────
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
(entry: FileEntry, index: number, totalCount: number) => {
|
(entry: FileEntry) => {
|
||||||
// Determine if file is viewable (has fileKey for Kintone files OR storagePath for local files)
|
// Determine if file is viewable (has fileKey for Kintone files OR storagePath for local files)
|
||||||
const isViewable = entry.fileKey || entry.storagePath;
|
const isViewable = entry.fileKey || entry.storagePath;
|
||||||
const isLast = index === totalCount - 1;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileItem
|
<FileItem
|
||||||
@@ -300,7 +299,6 @@ const FileSection: React.FC<FileSectionProps> = ({
|
|||||||
onView={isViewable ? () => onView(entry.fileKey, entry.fileName, entry.storagePath) : undefined}
|
onView={isViewable ? () => onView(entry.fileKey, entry.fileName, entry.storagePath) : undefined}
|
||||||
onDownload={entry.fileKey ? () => onDownload(entry.fileKey!, entry.fileName) : undefined}
|
onDownload={entry.fileKey ? () => onDownload(entry.fileKey!, entry.fileName) : undefined}
|
||||||
isDownloading={downloadingKey === entry.fileKey}
|
isDownloading={downloadingKey === entry.fileKey}
|
||||||
showDivider={!isLast}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -367,7 +365,7 @@ const FileSection: React.FC<FileSectionProps> = ({
|
|||||||
{files.length === 0 ? (
|
{files.length === 0 ? (
|
||||||
<div className={styles.emptySection}>{t("noConfig")}</div>
|
<div className={styles.emptySection}>{t("noConfig")}</div>
|
||||||
) : (
|
) : (
|
||||||
<SortableFileList items={files} onReorder={handleReorder} renderItem={renderItem} />
|
<SortableFileList items={files} onReorder={handleReorder} renderItem={renderItem} dividerColor={token.colorBorder} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Click-to-add strip */}
|
{/* Click-to-add strip */}
|
||||||
|
|||||||
@@ -10,16 +10,39 @@ import { SortableList } from "@lobehub/ui";
|
|||||||
import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent } from "@dnd-kit/core";
|
import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent } from "@dnd-kit/core";
|
||||||
import { SortableContext, verticalListSortingStrategy, arrayMove, sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
import { SortableContext, verticalListSortingStrategy, arrayMove, sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
||||||
import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers";
|
import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers";
|
||||||
|
import { createStyles } from "antd-style";
|
||||||
import type { FileEntry } from "@renderer/stores";
|
import type { FileEntry } from "@renderer/stores";
|
||||||
|
|
||||||
|
const useStyles = createStyles(({ css }) => ({
|
||||||
|
fileList: css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
/* 分割线:非最后一个子元素的 .fileItem 显示 */
|
||||||
|
& > *:not(:last-child) [data-file-item="true"]::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--divider-color, #d9d9d9);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}));
|
||||||
|
|
||||||
// ── SortableFileList Component ────────────────────────────────────────────────
|
// ── SortableFileList Component ────────────────────────────────────────────────
|
||||||
interface SortableFileListProps {
|
interface SortableFileListProps {
|
||||||
items: FileEntry[];
|
items: FileEntry[];
|
||||||
onReorder: (newOrder: string[], draggedItemId: string) => void;
|
onReorder: (newOrder: string[], draggedItemId: string) => void;
|
||||||
renderItem: (entry: FileEntry, index: number, totalCount: number) => React.ReactNode;
|
renderItem: (entry: FileEntry) => React.ReactNode;
|
||||||
|
dividerColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SortableFileList: React.FC<SortableFileListProps> = ({ items, onReorder, renderItem }) => {
|
const SortableFileList: React.FC<SortableFileListProps> = ({ items, onReorder, renderItem, dividerColor }) => {
|
||||||
|
const { styles } = useStyles();
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor),
|
useSensor(PointerSensor),
|
||||||
useSensor(KeyboardSensor, {
|
useSensor(KeyboardSensor, {
|
||||||
@@ -45,11 +68,13 @@ const SortableFileList: React.FC<SortableFileListProps> = ({ items, onReorder, r
|
|||||||
return (
|
return (
|
||||||
<DndContext sensors={sensors} collisionDetection={undefined} onDragEnd={handleDragEnd} modifiers={[restrictToVerticalAxis, restrictToWindowEdges]}>
|
<DndContext sensors={sensors} collisionDetection={undefined} onDragEnd={handleDragEnd} modifiers={[restrictToVerticalAxis, restrictToWindowEdges]}>
|
||||||
<SortableContext items={items} strategy={verticalListSortingStrategy}>
|
<SortableContext items={items} strategy={verticalListSortingStrategy}>
|
||||||
{items.map((entry, index) => (
|
<div className={styles.fileList} style={dividerColor ? ({ "--divider-color": dividerColor } as React.CSSProperties) : undefined}>
|
||||||
|
{items.map((entry) => (
|
||||||
<SortableList.Item key={entry.id} id={entry.id}>
|
<SortableList.Item key={entry.id} id={entry.id}>
|
||||||
{renderItem(entry, index, items.length)}
|
{renderItem(entry)}
|
||||||
</SortableList.Item>
|
</SortableList.Item>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user