diff --git a/app/components/Button.tsx b/app/components/Button.tsx
index faf7dd607..e8171c2f5 100644
--- a/app/components/Button.tsx
+++ b/app/components/Button.tsx
@@ -41,7 +41,8 @@ const RealButton = styled.button<{
border: 0;
}
- &:hover:not(:disabled) {
+ &:hover:not(:disabled),
+ &[aria-expanded="true"] {
background: ${(props) => darken(0.05, props.theme.buttonBackground)};
}
@@ -76,7 +77,8 @@ const RealButton = styled.button<{
}
- &:hover:not(:disabled) {
+ &:hover:not(:disabled),
+ &[aria-expanded="true"] {
background: ${
props.borderOnHover
? props.theme.buttonNeutralBackground
@@ -103,7 +105,8 @@ const RealButton = styled.button<{
background: ${props.theme.danger};
color: ${props.theme.white};
- &:hover:not(:disabled) {
+ &:hover:not(:disabled),
+ &[aria-expanded="true"] {
background: ${darken(0.05, props.theme.danger)};
}
diff --git a/app/components/DocumentListItem.tsx b/app/components/DocumentListItem.tsx
index a62aec441..e8426cebb 100644
--- a/app/components/DocumentListItem.tsx
+++ b/app/components/DocumentListItem.tsx
@@ -12,6 +12,7 @@ import DocumentMeta from "~/components/DocumentMeta";
import EventBoundary from "~/components/EventBoundary";
import Flex from "~/components/Flex";
import Highlight from "~/components/Highlight";
+import NudeButton from "~/components/NudeButton";
import StarButton, { AnimatedStar } from "~/components/Star";
import Tooltip from "~/components/Tooltip";
import useBoolean from "~/hooks/useBoolean";
@@ -171,6 +172,13 @@ const Actions = styled(EventBoundary)`
flex-shrink: 0;
flex-grow: 0;
+ ${NudeButton} {
+ &:hover,
+ &[aria-expanded="true"] {
+ background: ${(props) => props.theme.sidebarControlHoverBackground};
+ }
+ }
+
${breakpoint("tablet")`
display: flex;
`};
diff --git a/app/components/Sidebar/components/CollectionLink.tsx b/app/components/Sidebar/components/CollectionLink.tsx
index 5e2f1c01d..da094d60d 100644
--- a/app/components/Sidebar/components/CollectionLink.tsx
+++ b/app/components/Sidebar/components/CollectionLink.tsx
@@ -1,22 +1,26 @@
import fractionalIndex from "fractional-index";
import { observer } from "mobx-react";
+import { PlusIcon } from "outline-icons";
import * as React from "react";
import { useDrop, useDrag, DropTargetMonitor } from "react-dnd";
import { useTranslation } from "react-i18next";
-import { useLocation, useHistory } from "react-router-dom";
+import { useLocation, useHistory, Link } from "react-router-dom";
import styled from "styled-components";
import { sortNavigationNodes } from "@shared/utils/collections";
import Collection from "~/models/Collection";
import Document from "~/models/Document";
import DocumentReparent from "~/scenes/DocumentReparent";
import CollectionIcon from "~/components/CollectionIcon";
+import Fade from "~/components/Fade";
import Modal from "~/components/Modal";
+import NudeButton from "~/components/NudeButton";
+import Tooltip from "~/components/Tooltip";
import useBoolean from "~/hooks/useBoolean";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import CollectionMenu from "~/menus/CollectionMenu";
-import CollectionSortMenu from "~/menus/CollectionSortMenu";
import { NavigationNode } from "~/types";
+import { newDocumentPath } from "~/utils/routeHelpers";
import DocumentLink from "./DocumentLink";
import DropCursor from "./DropCursor";
import DropToImport from "./DropToImport";
@@ -254,20 +258,25 @@ function CollectionLink({
menu={
!isEditing &&
!isDraggingAnyCollection && (
- <>
- {can.update && displayDocumentLinks && (
-
+
+ {can.update && (
+
+
+
+
+
)}
- >
+
)
}
/>
diff --git a/app/components/Sidebar/components/DocumentLink.tsx b/app/components/Sidebar/components/DocumentLink.tsx
index 2d4285428..d903a423a 100644
--- a/app/components/Sidebar/components/DocumentLink.tsx
+++ b/app/components/Sidebar/components/DocumentLink.tsx
@@ -11,8 +11,10 @@ import Collection from "~/models/Collection";
import Document from "~/models/Document";
import Fade from "~/components/Fade";
import NudeButton from "~/components/NudeButton";
+import Tooltip from "~/components/Tooltip";
import useBoolean from "~/hooks/useBoolean";
import useStores from "~/hooks/useStores";
+import useToasts from "~/hooks/useToasts";
import DocumentMenu from "~/menus/DocumentMenu";
import { NavigationNode } from "~/types";
import { newDocumentPath } from "~/utils/routeHelpers";
@@ -47,6 +49,7 @@ function DocumentLink(
}: Props,
ref: React.RefObject
) {
+ const { showToast } = useToasts();
const { documents, policies } = useStores();
const { t } = useTranslation();
const isActiveDocument = activeDocument && activeDocument.id === node.id;
@@ -225,6 +228,19 @@ function DocumentLink(
const [{ isOverReorder, isDraggingAnyDocument }, dropToReorder] = useDrop({
accept: "document",
drop: (item: DragObject) => {
+ if (!manualSort) {
+ showToast(
+ t(
+ "You can't reorder documents in an alphabetically sorted collection"
+ ),
+ {
+ type: "info",
+ timeout: 5000,
+ }
+ );
+ return;
+ }
+
if (!collection) {
return;
}
@@ -327,16 +343,18 @@ function DocumentLink(
!isDraggingAnyDocument ? (
{can.createChildDocument && (
-
-
-
+
+
+
+
+
)}
- {manualSort && isDraggingAnyDocument && (
-
+ {isDraggingAnyDocument && (
+
)}
{openedOnce && (
diff --git a/app/components/Sidebar/components/DropCursor.tsx b/app/components/Sidebar/components/DropCursor.tsx
index c4b4bd443..d8bab427a 100644
--- a/app/components/Sidebar/components/DropCursor.tsx
+++ b/app/components/Sidebar/components/DropCursor.tsx
@@ -1,20 +1,30 @@
import * as React from "react";
import styled from "styled-components";
-function DropCursor({
- isActiveDrop,
- innerRef,
- position,
-}: {
+type Props = {
+ disabled?: boolean;
isActiveDrop: boolean;
innerRef: React.Ref;
position?: "top";
-}) {
- return ;
+};
+
+function DropCursor({ isActiveDrop, innerRef, position, disabled }: Props) {
+ return (
+
+ );
}
// transparent hover zone with a thin visible band vertically centered
-const Cursor = styled.div<{ isOver?: boolean; position?: "top" }>`
+const Cursor = styled.div<{
+ isOver?: boolean;
+ disabled?: boolean;
+ position?: "top";
+}>`
opacity: ${(props) => (props.isOver ? 1 : 0)};
transition: opacity 150ms;
position: absolute;
@@ -26,7 +36,10 @@ const Cursor = styled.div<{ isOver?: boolean; position?: "top" }>`
${(props) => (props.position === "top" ? "top: -7px;" : "bottom: -7px;")}
::after {
- background: ${(props) => props.theme.slateDark};
+ background: ${(props) =>
+ props.disabled
+ ? props.theme.sidebarActiveBackground
+ : props.theme.slateDark};
position: absolute;
top: 6px;
content: "";
diff --git a/app/components/Sidebar/components/SidebarButton.tsx b/app/components/Sidebar/components/SidebarButton.tsx
index 1307ea63e..fc3378f98 100644
--- a/app/components/Sidebar/components/SidebarButton.tsx
+++ b/app/components/Sidebar/components/SidebarButton.tsx
@@ -71,7 +71,8 @@ const Wrapper = styled(Flex)<{ minHeight: number }>`
cursor: pointer;
&:active,
- &:hover {
+ &:hover,
+ &[aria-expanded="true"] {
color: ${(props) => props.theme.sidebarText};
transition: background 100ms ease-in-out;
background: ${(props) => props.theme.sidebarActiveBackground};
diff --git a/app/components/Sidebar/components/SidebarLink.tsx b/app/components/Sidebar/components/SidebarLink.tsx
index 21b57ea19..88cf5a39f 100644
--- a/app/components/Sidebar/components/SidebarLink.tsx
+++ b/app/components/Sidebar/components/SidebarLink.tsx
@@ -190,13 +190,12 @@ const Link = styled(NavLink)<{ $isActiveDrop?: boolean; $isDraft?: boolean }>`
& + ${Actions} {
${NudeButton} {
- background: ${(props) => props.theme.sidebarBackground};
- }
- }
+ background: transparent;
- &[aria-current="page"] + ${Actions} {
- ${NudeButton} {
- background: ${(props) => props.theme.sidebarActiveBackground};
+ &:hover,
+ &[aria-expanded="true"] {
+ background: ${(props) => props.theme.sidebarControlHoverBackground};
+ }
}
}
diff --git a/app/menus/CollectionMenu.tsx b/app/menus/CollectionMenu.tsx
index 39d8e9d5f..9ad476231 100644
--- a/app/menus/CollectionMenu.tsx
+++ b/app/menus/CollectionMenu.tsx
@@ -6,6 +6,8 @@ import {
ImportIcon,
ExportIcon,
PadlockIcon,
+ AlphabeticalSortIcon,
+ ManualSortIcon,
} from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
@@ -124,6 +126,20 @@ function CollectionMenu({
[history, showToast, collection.id, documents]
);
+ const handleChangeSort = React.useCallback(
+ (field: string) => {
+ menu.hide();
+ return collection.save({
+ sort: {
+ field,
+ direction: "asc",
+ },
+ });
+ },
+ [collection, menu]
+ );
+
+ const alphabeticalSort = collection.sort.field === "title";
const can = usePolicy(collection.id);
const canUserInTeam = usePolicy(team.id);
const items: MenuItem[] = React.useMemo(
@@ -145,6 +161,30 @@ function CollectionMenu({
{
type: "separator",
},
+ {
+ type: "submenu",
+ title: t("Sort in sidebar"),
+ visible: can.update,
+ icon: alphabeticalSort ? (
+
+ ) : (
+
+ ),
+ items: [
+ {
+ type: "button",
+ title: t("Alphabetical sort"),
+ onClick: () => handleChangeSort("title"),
+ selected: alphabeticalSort,
+ },
+ {
+ type: "button",
+ title: t("Manual sort"),
+ onClick: () => handleChangeSort("index"),
+ selected: !alphabeticalSort,
+ },
+ ],
+ },
{
type: "button",
title: `${t("Edit")}…`,
@@ -182,6 +222,8 @@ function CollectionMenu({
t,
can.update,
can.delete,
+ alphabeticalSort,
+ handleChangeSort,
handleNewDocument,
handleImportDocument,
collection,
diff --git a/app/menus/CollectionSortMenu.tsx b/app/menus/CollectionSortMenu.tsx
deleted file mode 100644
index 151fdc4ef..000000000
--- a/app/menus/CollectionSortMenu.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { observer } from "mobx-react";
-import { AlphabeticalSortIcon, ManualSortIcon } from "outline-icons";
-import * as React from "react";
-import { useTranslation } from "react-i18next";
-import { useMenuState, MenuButton } from "reakit/Menu";
-import Collection from "~/models/Collection";
-import ContextMenu from "~/components/ContextMenu";
-import Template from "~/components/ContextMenu/Template";
-import NudeButton from "~/components/NudeButton";
-
-type Props = {
- collection: Collection;
- onOpen?: () => void;
- onClose?: () => void;
-};
-
-function CollectionSortMenu({ collection, onOpen, onClose }: Props) {
- const { t } = useTranslation();
- const menu = useMenuState({
- modal: true,
- });
- const handleChangeSort = React.useCallback(
- (field: string) => {
- menu.hide();
- return collection.save({
- sort: {
- field,
- direction: "asc",
- },
- });
- },
- [collection, menu]
- );
- const alphabeticalSort = collection.sort.field === "title";
-
- return (
- <>
-
- {(props) => (
-
- {alphabeticalSort ? : }
-
- )}
-
-
- handleChangeSort("title"),
- selected: alphabeticalSort,
- },
- {
- type: "button",
- title: t("Manual sort"),
- onClick: () => handleChangeSort("index"),
- selected: !alphabeticalSort,
- },
- ]}
- />
-
- >
- );
-}
-
-export default observer(CollectionSortMenu);
diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json
index ff820c50b..06c2afdd9 100644
--- a/shared/i18n/locales/en_US/translation.json
+++ b/shared/i18n/locales/en_US/translation.json
@@ -139,6 +139,7 @@
"Document archived": "Document archived",
"Move document": "Move document",
"Collections": "Collections",
+ "You can't reorder documents in an alphabetically sorted collection": "You can't reorder documents in an alphabetically sorted collection",
"Untitled": "Untitled",
"New nested document": "New nested document",
"Document not supported – try Markdown, Plain text, HTML, or Word": "Document not supported – try Markdown, Plain text, HTML, or Word",
@@ -231,16 +232,15 @@
"Path to document": "Path to document",
"Group member options": "Group member options",
"Remove": "Remove",
+ "Sort in sidebar": "Sort in sidebar",
+ "Alphabetical sort": "Alphabetical sort",
+ "Manual sort": "Manual sort",
"Edit": "Edit",
"Permissions": "Permissions",
"Delete": "Delete",
"Collection permissions": "Collection permissions",
"Delete collection": "Delete collection",
"Export collection": "Export collection",
- "Show sort menu": "Show sort menu",
- "Sort in sidebar": "Sort in sidebar",
- "Alphabetical sort": "Alphabetical sort",
- "Manual sort": "Manual sort",
"Document restored": "Document restored",
"Document unpublished": "Document unpublished",
"Document options": "Document options",
diff --git a/shared/theme.ts b/shared/theme.ts
index 3cea1be5d..3be9638a1 100644
--- a/shared/theme.ts
+++ b/shared/theme.ts
@@ -135,7 +135,7 @@ export const light = {
placeholder: "#a2b2c3",
sidebarBackground: colors.warmGrey,
sidebarActiveBackground: "#d7e0ea",
- sidebarControlHoverBackground: "rgba(0,0,0,0.1)",
+ sidebarControlHoverBackground: "rgb(138 164 193 / 20%)",
sidebarDraftBorder: darken("0.25", colors.warmGrey),
sidebarText: "rgb(78, 92, 110)",
backdrop: "rgba(0, 0, 0, 0.2)",