Files
outline/app/components/Sidebar/components/StarredLink.tsx
Saumya Pandey 4c95674ef0 fix: Add ability to collapse and expand collections that are not active (#3102)
* fix: add disclosure and transition

* fix: keep collections expanded

* fix: tune transition and collapsing conditions

* fix: collectionIcon expanded props is no longer driven by expanded state

* fix: sync issue

* fix: managing state together

* fix: remove comment

* fix: simplify expanded state

* fix: remove extra state

* fix: remove animation and retain expanded state

* fix: remove isCollectionDropped

* fix: don't use ref

* review suggestions

* fix many functional and design issues

* don't render every single document in the sidebar, just ones that the user has seen before

* chore: Sidebar refinement (#3154)

* stash

* wip: More sidebar tweaks

* Simplify draft bubble

* disclosure refactor

* wip wip

* lint

* tweak menu position

* Use document emoji for starred docs where available

* feat: Trigger cmd+k from sidebar (#3149)

* feat: Trigger cmd+k from sidebar

* Add hint when opening command bar from sidebar

* fix: Clicking internal links in shared documents sometimes reroutes to Login

* fix: Spacing issues on connected slack channels list

* Merge

* fix: Do not prefetch JS bundles on public share links

* fix: Buttons show on collection empty state when user does not have permission to edit

* fix: the hover area for the "collections" subheading was being obfuscated by the initial collection drop cursor

* fix: top-align disclosures

* fix: Disclosure color PR feedback
fix: Starred no longer draggable

* fix: Overflow on sidebar button

* fix: Scrollbar in sidebar when command menu is open

* Minor alignment issues, clarify back in settings sidebar

* fix: Fade component causes SidebarButton missizing

Co-authored-by: Nan Yu <thenanyu@gmail.com>

Co-authored-by: Tom Moor <tom.moor@gmail.com>
Co-authored-by: Nan Yu <thenanyu@gmail.com>
2022-02-23 21:26:38 -08:00

158 lines
4.2 KiB
TypeScript

import fractionalIndex from "fractional-index";
import { observer } from "mobx-react";
import { StarredIcon } from "outline-icons";
import * as React from "react";
import { useEffect, useState } from "react";
import { useDrag, useDrop } from "react-dnd";
import styled, { useTheme } from "styled-components";
import parseTitle from "@shared/utils/parseTitle";
import Star from "~/models/Star";
import EmojiIcon from "~/components/EmojiIcon";
import Fade from "~/components/Fade";
import useBoolean from "~/hooks/useBoolean";
import useStores from "~/hooks/useStores";
import DocumentMenu from "~/menus/DocumentMenu";
import DropCursor from "./DropCursor";
import SidebarLink from "./SidebarLink";
type Props = {
star?: Star;
depth: number;
title: string;
to: string;
documentId: string;
collectionId: string;
};
function StarredLink({
depth,
to,
documentId,
title,
collectionId,
star,
}: Props) {
const theme = useTheme();
const { collections, documents } = useStores();
const collection = collections.get(collectionId);
const document = documents.get(documentId);
const [expanded, setExpanded] = useState(false);
const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
const childDocuments = collection
? collection.getDocumentChildren(documentId)
: [];
const hasChildDocuments = childDocuments.length > 0;
useEffect(() => {
async function load() {
if (!document) {
await documents.fetch(documentId);
}
}
load();
}, [collection, collectionId, collections, document, documentId, documents]);
const handleDisclosureClick = React.useCallback(
(ev: React.MouseEvent<HTMLButtonElement>) => {
ev.preventDefault();
ev.stopPropagation();
setExpanded((prevExpanded) => !prevExpanded);
},
[]
);
// Draggable
const [{ isDragging }, drag] = useDrag({
type: "star",
item: () => star,
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
canDrag: () => {
return depth === 0;
},
});
// Drop to reorder
const [{ isOverReorder, isDraggingAny }, dropToReorder] = useDrop({
accept: "star",
drop: (item: Star) => {
const next = star?.next();
item?.save({
index: fractionalIndex(star?.index || null, next?.index || null),
});
},
collect: (monitor) => ({
isOverReorder: !!monitor.isOver(),
isDraggingAny: !!monitor.canDrop(),
}),
});
const { emoji } = parseTitle(title);
const label = emoji ? title.replace(emoji, "") : title;
return (
<>
<Draggable key={documentId} ref={drag} $isDragging={isDragging}>
<SidebarLink
depth={depth}
expanded={hasChildDocuments ? expanded : undefined}
onDisclosureClick={handleDisclosureClick}
to={`${to}?starred`}
icon={
depth === 0 ? (
emoji ? (
<EmojiIcon emoji={emoji} />
) : (
<StarredIcon color={theme.yellow} />
)
) : undefined
}
isActive={(match, location) =>
!!match && location.search === "?starred"
}
label={depth === 0 ? label : title}
exact={false}
showActions={menuOpen}
menu={
document ? (
<Fade>
<DocumentMenu
document={document}
onOpen={handleMenuOpen}
onClose={handleMenuClose}
/>
</Fade>
) : undefined
}
/>
{isDraggingAny && (
<DropCursor isActiveDrop={isOverReorder} innerRef={dropToReorder} />
)}
</Draggable>
{expanded &&
childDocuments.map((childDocument) => (
<ObserveredStarredLink
key={childDocument.id}
depth={depth === 0 ? 2 : depth + 1}
title={childDocument.title}
to={childDocument.url}
documentId={childDocument.id}
collectionId={collectionId}
/>
))}
</>
);
}
const Draggable = styled.div<{ $isDragging?: boolean }>`
position: relative;
opacity: ${(props) => (props.$isDragging ? 0.5 : 1)};
`;
const ObserveredStarredLink = observer(StarredLink);
export default ObserveredStarredLink;