mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-09 23:15:36 +00:00
feat: add dropdown for links and collection header
This commit is contained in:
@@ -1,34 +1,28 @@
|
||||
import PATHS from '#constants/paths';
|
||||
import styled from '@emotion/styled';
|
||||
import QuickResourceAction from '~/components/dashboard/quick_action/quick_action';
|
||||
import useActiveCollection from '~/hooks/use_active_collection';
|
||||
import { BsThreeDotsVertical } from 'react-icons/bs';
|
||||
import { HiOutlinePencil } from 'react-icons/hi2';
|
||||
import { IoIosAddCircleOutline } from 'react-icons/io';
|
||||
import { IoTrashOutline } from 'react-icons/io5';
|
||||
import Dropdown from '~/components/common/dropdown/dropdown';
|
||||
import { DropdownItemLink } from '~/components/common/dropdown/dropdown_item';
|
||||
|
||||
const CollectionControlsStyle = styled.span({
|
||||
display: 'flex',
|
||||
gap: '0.5em',
|
||||
alignItems: 'center',
|
||||
});
|
||||
const DeleteItem = styled(DropdownItemLink)(({ theme }) => ({
|
||||
color: theme.colors.lightRed,
|
||||
}));
|
||||
|
||||
export default function CollectionControls() {
|
||||
const { activeCollection } = useActiveCollection();
|
||||
return (
|
||||
activeCollection && (
|
||||
<CollectionControlsStyle>
|
||||
<QuickResourceAction
|
||||
resource="link"
|
||||
action="create"
|
||||
collectionId={activeCollection.id}
|
||||
/>
|
||||
<QuickResourceAction
|
||||
resource="collection"
|
||||
action="edit"
|
||||
resourceId={activeCollection.id}
|
||||
/>
|
||||
<QuickResourceAction
|
||||
resource="collection"
|
||||
action="remove"
|
||||
resourceId={activeCollection.id}
|
||||
/>
|
||||
</CollectionControlsStyle>
|
||||
)
|
||||
);
|
||||
}
|
||||
const CollectionControls = () => (
|
||||
<Dropdown label={<BsThreeDotsVertical />}>
|
||||
<DropdownItemLink href={PATHS.LINK.CREATE}>
|
||||
<IoIosAddCircleOutline /> Add
|
||||
</DropdownItemLink>
|
||||
<DropdownItemLink href={PATHS.COLLECTION.EDIT}>
|
||||
<HiOutlinePencil /> Edit
|
||||
</DropdownItemLink>
|
||||
<DeleteItem href={PATHS.COLLECTION.REMOVE}>
|
||||
<IoTrashOutline /> Delete
|
||||
</DeleteItem>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
export default CollectionControls;
|
||||
|
||||
98
inertia/components/dashboard/link_list/link_controls.tsx
Normal file
98
inertia/components/dashboard/link_list/link_controls.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import PATHS from '#constants/paths';
|
||||
import type Link from '#models/link';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useCallback } from 'react';
|
||||
import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
|
||||
import { BsThreeDotsVertical } from 'react-icons/bs';
|
||||
import { HiOutlinePencil } from 'react-icons/hi2';
|
||||
import { IoTrashOutline } from 'react-icons/io5';
|
||||
import Dropdown from '~/components/common/dropdown/dropdown';
|
||||
import {
|
||||
DropdownItemButton,
|
||||
DropdownItemLink,
|
||||
} from '~/components/common/dropdown/dropdown_item';
|
||||
import useCollections from '~/hooks/use_collections';
|
||||
import { appendCollectionId } from '~/lib/navigation';
|
||||
import { makeRequest } from '~/lib/request';
|
||||
|
||||
const StartItem = styled(DropdownItemButton)(({ theme }) => ({
|
||||
color: theme.colors.yellow,
|
||||
}));
|
||||
|
||||
const DeleteItem = styled(DropdownItemLink)(({ theme }) => ({
|
||||
color: theme.colors.lightRed,
|
||||
}));
|
||||
|
||||
export default function LinkControls({ link }: { link: Link }) {
|
||||
const theme = useTheme();
|
||||
const { collections, setCollections } = useCollections();
|
||||
|
||||
const toggleFavorite = useCallback(
|
||||
(linkId: Link['id']) => {
|
||||
let linkIndex = 0;
|
||||
const collectionIndex = collections.findIndex(({ links }) => {
|
||||
const lIndex = links.findIndex((l) => l.id === linkId);
|
||||
if (lIndex !== -1) {
|
||||
linkIndex = lIndex;
|
||||
}
|
||||
return lIndex !== -1;
|
||||
});
|
||||
|
||||
const collectionLink = collections[collectionIndex].links[linkIndex];
|
||||
const collectionsCopy = [...collections];
|
||||
collectionsCopy[collectionIndex].links[linkIndex] = {
|
||||
...collectionLink,
|
||||
favorite: !collectionLink.favorite,
|
||||
};
|
||||
|
||||
setCollections(collectionsCopy);
|
||||
},
|
||||
[collections, setCollections]
|
||||
);
|
||||
|
||||
const onFavorite = () => {
|
||||
makeRequest({
|
||||
url: `${PATHS.API.LINK}/${link.id}`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
name: link.name,
|
||||
url: link.url,
|
||||
favorite: !link.favorite,
|
||||
collectionId: link.collectionId,
|
||||
},
|
||||
})
|
||||
.then(() => toggleFavorite(link.id))
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
console.log(link.favorite, link.favorite ? 'oui' : 'non');
|
||||
return (
|
||||
<Dropdown
|
||||
label={<BsThreeDotsVertical />}
|
||||
css={{ backgroundColor: theme.colors.secondary }}
|
||||
>
|
||||
<StartItem onClick={onFavorite}>
|
||||
{!link.favorite ? (
|
||||
<>
|
||||
<AiFillStar /> Add to favorites
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AiOutlineStar /> Remove from favorites
|
||||
</>
|
||||
)}
|
||||
</StartItem>
|
||||
<DropdownItemLink
|
||||
href={appendCollectionId(PATHS.LINK.EDIT, link.collectionId)}
|
||||
>
|
||||
<HiOutlinePencil /> Edit
|
||||
</DropdownItemLink>
|
||||
<DeleteItem
|
||||
href={appendCollectionId(PATHS.LINK.REMOVE, link.collectionId)}
|
||||
>
|
||||
<IoTrashOutline /> Delete
|
||||
</DeleteItem>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,9 @@
|
||||
import PATHS from '#constants/paths';
|
||||
import type Link from '#models/link';
|
||||
import styled from '@emotion/styled';
|
||||
import { useCallback } from 'react';
|
||||
import { AiFillStar } from 'react-icons/ai';
|
||||
import ExternalLink from '~/components/common/external_link';
|
||||
import LinkFavicon from '~/components/dashboard/link/link_favicon';
|
||||
import QuickResourceAction from '~/components/dashboard/quick_action/quick_action';
|
||||
import QuickLinkFavorite from '~/components/dashboard/quick_action/quick_favorite_link';
|
||||
import useCollections from '~/hooks/use_collections';
|
||||
import { makeRequest } from '~/lib/request';
|
||||
import LinkControls from '~/components/dashboard/link_list/link_controls';
|
||||
|
||||
const LinkWrapper = styled.li(({ theme }) => ({
|
||||
userSelect: 'none',
|
||||
@@ -20,16 +15,21 @@ const LinkWrapper = styled.li(({ theme }) => ({
|
||||
padding: '0.75em 1em',
|
||||
border: `1px solid ${theme.colors.lightGrey}`,
|
||||
borderRadius: theme.border.radius,
|
||||
outline: '3px solid transparent',
|
||||
|
||||
'&:hover': {
|
||||
outlineWidth: '1px',
|
||||
outlineStyle: 'solid',
|
||||
},
|
||||
}));
|
||||
|
||||
const LinkHeader = styled.div(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: '1em',
|
||||
alignItems: 'center',
|
||||
|
||||
'& > a': {
|
||||
height: '100%',
|
||||
maxWidth: 'calc(100% - 125px)', // TODO: fix this, it is ugly af :(
|
||||
maxWidth: 'calc(100% - 75px)', // TODO: fix this, it is ugly af :(
|
||||
textDecoration: 'none',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
@@ -49,22 +49,6 @@ const LinkName = styled.div({
|
||||
overflow: 'hidden',
|
||||
});
|
||||
|
||||
const LinkControls = styled.div({
|
||||
display: 'none',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '10px',
|
||||
|
||||
'& svg': {
|
||||
height: '20px',
|
||||
width: '20px',
|
||||
},
|
||||
|
||||
'&:hover *': {
|
||||
transform: 'scale(1.3)',
|
||||
},
|
||||
});
|
||||
|
||||
const LinkDescription = styled.div(({ theme }) => ({
|
||||
marginTop: '0.5em',
|
||||
color: theme.colors.font,
|
||||
@@ -97,46 +81,6 @@ export default function LinkItem({
|
||||
showUserControls: boolean;
|
||||
}) {
|
||||
const { id, name, url, description, favorite } = link;
|
||||
const { collections, setCollections } = useCollections();
|
||||
|
||||
const toggleFavorite = useCallback(
|
||||
(linkId: Link['id']) => {
|
||||
let linkIndex = 0;
|
||||
const collectionIndex = collections.findIndex(({ links }) => {
|
||||
const lIndex = links.findIndex((l) => l.id === linkId);
|
||||
if (lIndex !== -1) {
|
||||
linkIndex = lIndex;
|
||||
}
|
||||
return lIndex !== -1;
|
||||
});
|
||||
|
||||
const collectionLink = collections[collectionIndex].links[linkIndex];
|
||||
const collectionsCopy = [...collections];
|
||||
collectionsCopy[collectionIndex].links[linkIndex] = {
|
||||
...collectionLink,
|
||||
favorite: !collectionLink.favorite,
|
||||
};
|
||||
|
||||
setCollections(collectionsCopy);
|
||||
},
|
||||
[collections, setCollections]
|
||||
);
|
||||
|
||||
const onFavorite = () => {
|
||||
makeRequest({
|
||||
url: `${PATHS.API.LINK}/${link.id}`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
name,
|
||||
url,
|
||||
favorite: !favorite,
|
||||
collectionId: link.collectionId,
|
||||
},
|
||||
})
|
||||
.then(() => toggleFavorite(link.id))
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
return (
|
||||
<LinkWrapper key={id}>
|
||||
<LinkHeader>
|
||||
@@ -147,21 +91,7 @@ export default function LinkItem({
|
||||
</LinkName>
|
||||
<LinkItemURL url={url} />
|
||||
</ExternalLink>
|
||||
{showUserControls && (
|
||||
<LinkControls>
|
||||
<QuickLinkFavorite onClick={onFavorite} isFavorite={favorite} />
|
||||
<QuickResourceAction
|
||||
resource="link"
|
||||
action="edit"
|
||||
resourceId={id}
|
||||
/>
|
||||
<QuickResourceAction
|
||||
resource="link"
|
||||
action="remove"
|
||||
resourceId={id}
|
||||
/>
|
||||
</LinkControls>
|
||||
)}
|
||||
{showUserControls && <LinkControls link={link} />}
|
||||
</LinkHeader>
|
||||
{description && <LinkDescription>{description}</LinkDescription>}
|
||||
</LinkWrapper>
|
||||
|
||||
@@ -19,8 +19,9 @@ const LinksWrapper = styled.div({
|
||||
});
|
||||
|
||||
const CollectionHeaderWrapper = styled.h2(({ theme }) => ({
|
||||
fontWeight: 400,
|
||||
color: theme.colors.primary,
|
||||
fontWeight: 500,
|
||||
paddingInline: '1em',
|
||||
display: 'flex',
|
||||
gap: '0.4em',
|
||||
alignItems: 'center',
|
||||
|
||||
Reference in New Issue
Block a user