mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
feat: recreate collection list/item
This commit is contained in:
@@ -54,7 +54,7 @@ function LinkItemURL({ url }: { url: Link['url'] }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Text className={styles.linkUrl} color="gray" size="xs" lineClamp={1}>
|
||||
<Text className={styles.linkUrl} c="gray" size="xs" lineClamp={1}>
|
||||
{origin}
|
||||
<span className={styles.linkUrlPathname}>{text}</span>
|
||||
</Text>
|
||||
@@ -62,7 +62,7 @@ function LinkItemURL({ url }: { url: Link['url'] }) {
|
||||
} catch (error) {
|
||||
console.error('error', error);
|
||||
return (
|
||||
<Text className={styles.linkUrl} color="gray" size="xs" lineClamp={1}>
|
||||
<Text className={styles.linkUrl} c="gray" size="xs" lineClamp={1}>
|
||||
{url}
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
.link {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm);
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
font-weight: 500;
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-0),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
|
||||
|
||||
.linkIcon {
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
|
||||
}
|
||||
}
|
||||
|
||||
&[data-active] {
|
||||
&,
|
||||
&:hover {
|
||||
background-color: var(--mantine-color-blue-light);
|
||||
color: var(--mantine-color-blue-light-color);
|
||||
|
||||
.linkIcon {
|
||||
color: var(--mantine-color-blue-light-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.linkIcon {
|
||||
color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2));
|
||||
margin-right: var(--mantine-spacing-sm);
|
||||
width: rem(25px);
|
||||
height: rem(25px);
|
||||
min-width: rem(25px);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Link } from '@inertiajs/react';
|
||||
import { route } from '@izzyjs/route/client';
|
||||
import { Text } from '@mantine/core';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { AiFillFolderOpen, AiOutlineFolder } from 'react-icons/ai';
|
||||
import useActiveCollection from '~/hooks/use_active_collection';
|
||||
import { appendCollectionId } from '~/lib/navigation';
|
||||
import { CollectionWithLinks } from '~/types/app';
|
||||
import classes from './collection_item.module.css';
|
||||
|
||||
export default function CollectionItem({
|
||||
collection,
|
||||
}: {
|
||||
collection: CollectionWithLinks;
|
||||
}) {
|
||||
const itemRef = useRef<HTMLAnchorElement>(null);
|
||||
const { activeCollection } = useActiveCollection();
|
||||
const isActiveCollection = collection.id === activeCollection?.id;
|
||||
const FolderIcon = isActiveCollection ? AiFillFolderOpen : AiOutlineFolder;
|
||||
|
||||
useEffect(() => {
|
||||
if (collection.id === activeCollection?.id) {
|
||||
itemRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}, [collection.id, activeCollection?.id]);
|
||||
|
||||
const linksCount = activeCollection?.links.length ?? 0;
|
||||
return (
|
||||
<Link
|
||||
className={classes.link}
|
||||
data-active={isActiveCollection || undefined}
|
||||
href={appendCollectionId(route('dashboard').path, collection.id)}
|
||||
key={collection.id}
|
||||
ref={itemRef}
|
||||
>
|
||||
<FolderIcon className={classes.linkIcon} />
|
||||
<Text lineClamp={1} maw="160px">
|
||||
{collection.name}
|
||||
</Text>
|
||||
{linksCount > 0 && (
|
||||
<Text c="var(--mantine-color-gray-5)" ml="xs">
|
||||
— {linksCount}
|
||||
</Text>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
.sideMenu {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 0.35em;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.listContainer {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.collectionList {
|
||||
padding: 1px;
|
||||
padding-right: 5px;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 0.35em;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Box, ScrollArea, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useActiveCollection from '~/hooks/use_active_collection';
|
||||
import useCollections from '~/hooks/use_collections';
|
||||
import useShortcut from '~/hooks/use_shortcut';
|
||||
import CollectionItem from '~/mantine/components/dashboard/collection/item/collection_item';
|
||||
import styles from './collection_list.module.css';
|
||||
|
||||
export default function CollectionList() {
|
||||
const { t } = useTranslation('common');
|
||||
const { collections } = useCollections();
|
||||
const { activeCollection, setActiveCollection } = useActiveCollection();
|
||||
|
||||
const goToPreviousCollection = () => {
|
||||
const currentCategoryIndex = collections.findIndex(
|
||||
({ id }) => id === activeCollection?.id
|
||||
);
|
||||
if (currentCategoryIndex === -1 || currentCategoryIndex === 0) return;
|
||||
|
||||
setActiveCollection(collections[currentCategoryIndex - 1]);
|
||||
};
|
||||
|
||||
const goToNextCollection = () => {
|
||||
const currentCategoryIndex = collections.findIndex(
|
||||
({ id }) => id === activeCollection?.id
|
||||
);
|
||||
if (
|
||||
currentCategoryIndex === -1 ||
|
||||
currentCategoryIndex === collections.length - 1
|
||||
)
|
||||
return;
|
||||
|
||||
setActiveCollection(collections[currentCategoryIndex + 1]);
|
||||
};
|
||||
|
||||
useShortcut('ARROW_UP', goToPreviousCollection);
|
||||
useShortcut('ARROW_DOWN', goToNextCollection);
|
||||
|
||||
return (
|
||||
<Box className={styles.sideMenu}>
|
||||
<Box className={styles.listContainer}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Text c="dimmed" ml="md" mb="sm">
|
||||
{t('collection.collections', { count: collections.length })} •{' '}
|
||||
{collections.length}
|
||||
</Text>
|
||||
<ScrollArea className={styles.collectionList}>
|
||||
{collections.map((collection) => (
|
||||
<CollectionItem collection={collection} key={collection.id} />
|
||||
))}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
19
inertia/mantine/components/dashboard/dashboard_aside.tsx
Normal file
19
inertia/mantine/components/dashboard/dashboard_aside.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { AppShell, Burger, Group, ScrollArea, Text } from '@mantine/core';
|
||||
import CollectionList from '~/mantine/components/dashboard/collection/list/collection_list';
|
||||
|
||||
interface DashboardAsideProps {
|
||||
isOpen: boolean;
|
||||
toggle: () => void;
|
||||
}
|
||||
|
||||
export const DashboardAside = ({ isOpen, toggle }: DashboardAsideProps) => (
|
||||
<AppShell.Aside p="md">
|
||||
<Group justify="space-between" hiddenFrom="md" mb="md">
|
||||
<Text>Collections</Text>
|
||||
<Burger opened={isOpen} onClick={toggle} hiddenFrom="md" size="md" />
|
||||
</Group>
|
||||
<AppShell.Section grow component={ScrollArea}>
|
||||
<CollectionList />
|
||||
</AppShell.Section>
|
||||
</AppShell.Aside>
|
||||
);
|
||||
Reference in New Issue
Block a user