Skip to content

Commit

Permalink
给nav添加拖拽功能
Browse files Browse the repository at this point in the history
  • Loading branch information
master1lan committed Jan 8, 2023
1 parent f4990ef commit e92f8da
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 162 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
},
"dependencies": {
"@dnd-kit/core": "^6.0.7",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mui/icons-material": "^5.11.0",
Expand Down
30 changes: 30 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/components/image/image.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
overflow: hidden;
border-radius: 8px;
outline: 0.5px solid rgba(0, 0, 0, .05);
height: 100%;

&>img {
width: 100%;
height: 100%;
display: inline-block;
object-fit: cover;
transition: opacity .5s linear;
Expand Down
23 changes: 10 additions & 13 deletions src/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@
margin: 0;
}

// .feedContainer {
// margin: 0 auto;
// // max-width: 1200px;
// display: flex;
// align-items: flex-start;
// position: relative;
// width: 100%;
.feedContainer {
margin: 0 auto;
padding: 0 8px;
max-width: 1200px;

// .element-item {
// min-width: 200px;
// box-sizing: border-box;
// padding: 0 5px;
// }
// }
// .element-item {
// min-width: 200px;
// box-sizing: border-box;
// padding: 0 5px;
// }
}
256 changes: 162 additions & 94 deletions src/routers/layout/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,181 @@ import { Chip, Stack } from "@mui/material";
import { useInView } from "react-intersection-observer";
import SegmentIcon from "@mui/icons-material/Segment";
import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen";
import { NavLink } from "react-router-dom";
import {
Outlet,
NavLink,
useLoaderData,
Form,
redirect,
useLocation,
useSubmit,
} from "react-router-dom";
DndContext,
closestCenter,
MouseSensor,
TouchSensor,
useSensor,
useSensors,
DragEndEvent,
} from "@dnd-kit/core";
import { arrayMove, SortableContext, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import {
restrictToHorizontalAxis,
restrictToParentElement,
} from "@dnd-kit/modifiers";
import { nanoid } from "nanoid";
import styles from "./layout.module.less";
import { FC, forwardRef, useState, useEffect, useMemo } from "react";
import { FC, useState, useEffect, useMemo, memo } from "react";
import { Pick } from "@utils/index";

export default function Header_Nav() {
//todo 这里在loacalstorage中查找
const [navLists, setLists] = useState<NavListItemType[]>(nav_tag_list);
//最后一个span是否可见
const { ref, inView } = useInView({ initialInView: true });
//tag区是否展开
const [showed, setShow] = useState(false);
//拖拽事件绑定
const sensors = useSensors(
// 鼠标点击
useSensor(MouseSensor, {
activationConstraint: {
delay: 100,
tolerance: 5,
},
}),
// 触摸屏幕
useSensor(TouchSensor, {
activationConstraint: {
delay: 100,
tolerance: 5,
},
})
);
//拖拽事件完成事件
function handleDragEnd(event: DragEndEvent) {
const { active, over } = event;
if (over && active.id !== over.id) {
setLists((items) => {
const oldIndex = items.findIndex((item) => item.id === active.id);
const newIndex = items.findIndex((item) => item.id === over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}
const ComRes = useMemo(
() => (
<>
{navLists.map((item) => (
<NavItem key={item.id} {...item} />
))}
</>
),
[navLists]
);
return (
<div className={styles["nav"]}>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
modifiers={[restrictToParentElement]}
>
<Stack
direction='row'
alignItems='center'
className={`${styles["navstack"]}`}
style={{
display: showed ? "grid" : "flex",
}}
data-showed={showed}
>
<SortableContext items={navLists}>{ComRes}</SortableContext>
<span
ref={ref}
style={{
margin: 0,
width: "1px",
}}
></span>
<div
className={styles["nav-right-show-btn"]}
onClick={() => setShow((showed) => !showed)}
style={{
display: inView ? "none" : "flex",
}}
>
<SegmentIcon fontSize='medium' />
</div>
</Stack>
</DndContext>
</div>
);
}

const NavItem: FC<NavListItemType> = (props) => {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: props.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
{props.type === "router" ? (
<NavRouterChipItem
{...Pick(props as NavRouterItemType, "pathname", "name")}
/>
) : (
<NavTagChipItem {...Pick(props as NavQueryItemType, "query")} />
)}
</div>
);
};

const NavRouterChipItem: FC<Pick<NavRouterItemType, "pathname" | "name">> =
memo((props) => (
<NavLink
to={props.pathname}
className={({ isActive, isPending }) =>
`${styles["navlink"]} ${isActive ? styles["navlink-active"] : ""}`
}
>
<Chip
className={styles["navstack-filter-tag"]}
label={props.name}
clickable
/>
</NavLink>
));

const NavTagChipItem: FC<Pick<NavQueryItemType, "query">> = memo((props) => {
const [clicked, setClick] = useState<boolean>(false);
return (
<Chip
className={styles["navstack-filter-tag"]}
label={props.query}
color={clicked ? "info" : "default"}
onClick={() => setClick((clicked) => !clicked)}
/>
);
});
//todo:修复展示更多栏的bug

const router_lists = [
type NavRouterItemType = {
id: string;
type: "router";
pathname: string;
name: string;
};
type NavQueryItemType = { id: string; type: "query"; query: string };
type NavListItemType = NavQueryItemType | NavRouterItemType;

const nav_tag_list = [
{
id: nanoid(2),
type: "router",
pathname: "photo",
name: "图片",
},
{
id: nanoid(2),
type: "router",
pathname: "video",
name: "视频",
},
];

const nav_lists = [
{
type: "query",
query: "所有二创",
Expand Down Expand Up @@ -79,82 +225,4 @@ const nav_lists = [
type: "query",
query: "其他分区",
},
].map((item) => ({ ...item, id: nanoid(3) }));

export default function Header_Nav() {
//todo 这里存放到context中间去
const { ref, inView } = useInView({ initialInView: true });
const [showed, setShow] = useState(false);
console.log({ inView });
const ComRes = useMemo(
() => (
<>
{router_lists.map((router) => (
<NavLink
key={router.id}
to={router.pathname}
className={({ isActive, isPending }) =>
`${styles["navlink"]} ${isActive ? styles["navlink-active"] : ""}`
}
>
<Chip
className={styles["navstack-filter-tag"]}
label={router.name}
clickable
/>
</NavLink>
))}
{nav_lists.map((nav) => (
<ClickChip key={nav.id} label={nav.query} />
))}
</>
),
[]
);
return (
<div className={styles["nav"]}>
<Stack
direction='row'
alignItems='center'
className={`${styles["navstack"]}`}
style={{
display: showed ? "grid" : "flex",
}}
data-showed={showed}
>
{ComRes}
<span
ref={ref}
style={{
margin: 0,
width: "1px",
}}
></span>
<div
className={styles["nav-right-show-btn"]}
onClick={() => setShow((showed) => !showed)}
style={{
display: inView ? "none" : "flex",
}}
>
<SegmentIcon fontSize='medium' />
</div>
</Stack>
</div>
);
}

const ClickChip: FC<{ label: string }> = (props) => {
const [clicked, setClick] = useState<boolean>(false);
return (
//@ts-ignore
<Chip
className={styles["navstack-filter-tag"]}
{...props}
color={clicked ? "info" : "default"}
onClick={() => setClick((clicked) => !clicked)}
/>
);
};
//todo:修复展示更多栏的bug
//todo:改造nav栏,通过拖拽修改排序
].map((item) => ({ ...item, id: nanoid(3) })) as NavListItemType[];
6 changes: 5 additions & 1 deletion src/routers/video/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import VideoMasonry from "./masonry";
export default function VideoPage() {
return <>{/* <VideoMasonry /> */}</>;
return (
<>
<VideoMasonry />
</>
);
}
Loading

0 comments on commit e92f8da

Please sign in to comment.