Allow direct editing of collection icon (#7120)

* Allow direct editing of collection icon

* feedback
This commit is contained in:
Tom Moor
2024-06-23 17:26:00 -04:00
committed by GitHub
parent 2cad16d6b3
commit 95c768f444
5 changed files with 47 additions and 24 deletions

View File

@@ -2,7 +2,6 @@ import { observer } from "mobx-react";
import { CollectionIcon } from "outline-icons";
import { getLuminance } from "polished";
import * as React from "react";
import { randomElement } from "@shared/random";
import { colorPalette } from "@shared/utils/collections";
import Collection from "~/models/Collection";
import Icon from "~/components/Icon";
@@ -32,7 +31,7 @@ function ResolvedCollectionIcon({
if (!collection.icon || collection.icon === "collection") {
// If the chosen icon color is very dark then we invert it in dark mode
// otherwise it will be impossible to see against the dark background.
const collectionColor = collection.color ?? randomElement(colorPalette);
const collectionColor = collection.color ?? colorPalette[0];
const color =
inputColor ||
(ui.resolvedTheme === "dark" && collectionColor !== "currentColor"

View File

@@ -13,6 +13,7 @@ import {
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { s } from "@shared/styles";
import { colorPalette } from "@shared/utils/collections";
import Collection from "~/models/Collection";
import Search from "~/scenes/Search";
import { Action } from "~/components/Actions";
@@ -43,6 +44,8 @@ import Empty from "./components/Empty";
import MembershipPreview from "./components/MembershipPreview";
import ShareButton from "./components/ShareButton";
const IconPicker = React.lazy(() => import("~/components/IconPicker"));
function CollectionScene() {
const params = useParams<{ id?: string }>();
const history = useHistory();
@@ -60,6 +63,13 @@ function CollectionScene() {
collections.getByUrl(id) || collections.get(id);
const can = usePolicy(collection);
const handleIconChange = React.useCallback(
async (icon: string | null, color: string | null) => {
await collection?.save({ icon, color });
},
[collection]
);
React.useEffect(() => {
setLastVisitedPath(currentPath);
}, [currentPath, setLastVisitedPath]);
@@ -119,6 +129,10 @@ function CollectionScene() {
return <Search notFound />;
}
const fallbackIcon = collection ? (
<Icon as={CollectionIcon} collection={collection} size={40} expanded />
) : null;
return collection ? (
<Scene
// Forced mount prevents animation of pinned documents when navigating
@@ -164,7 +178,21 @@ function CollectionScene() {
) : (
<>
<HeadingWithIcon>
<HeadingIcon collection={collection} size={40} expanded />
{can.update ? (
<React.Suspense fallback={fallbackIcon}>
<Icon
icon={collection.icon ?? "collection"}
color={collection.color ?? colorPalette[0]}
initial={collection.name[0]}
size={40}
popoverPosition="bottom-start"
onChange={handleIconChange}
borderOnHover
/>
</React.Suspense>
) : (
fallbackIcon
)}
{collection.name}
{collection.isPrivate &&
!FeatureFlags.isEnabled(Feature.newCollectionSharing) && (
@@ -305,7 +333,7 @@ const HeadingWithIcon = styled(Heading)`
`};
`;
const HeadingIcon = styled(CollectionIcon)`
const Icon = styled(IconPicker)`
flex-shrink: 0;
margin-left: -8px;
margin-right: 8px;

View File

@@ -659,7 +659,7 @@ type EditorContainerProps = {
const EditorContainer = styled.div<EditorContainerProps>`
// Adds space to the gutter to make room for icon & heading annotations
padding: 0 44px;
padding: 0 40px;
${breakpoint("tablet")`
grid-row: 1;
@@ -686,7 +686,7 @@ type RevisionContainerProps = {
const RevisionContainer = styled.div<RevisionContainerProps>`
// Adds space to the gutter to make room for icon
padding: 0 44px;
padding: 0 40px;
${breakpoint("tablet")`
grid-row: 1;

View File

@@ -8,7 +8,7 @@ import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import isMarkdown from "@shared/editor/lib/isMarkdown";
import normalizePastedMarkdown from "@shared/editor/lib/markdown/normalize";
import { extraArea, s } from "@shared/styles";
import { s } from "@shared/styles";
import { light } from "@shared/styles/theme";
import {
getCurrentDateAsString,
@@ -138,21 +138,21 @@ const DocumentTitle = React.forwardRef(function _DocumentTitle(
);
const handleChange = React.useCallback(
(value: string) => {
let title = value;
(input: string) => {
let value = input;
if (/\/date\s$/.test(value)) {
title = getCurrentDateAsString();
if (/\/date\s$/.test(input)) {
value = getCurrentDateAsString();
ref?.current?.focusAtEnd();
} else if (/\/time$/.test(value)) {
title = getCurrentTimeAsString();
} else if (/\/time$/.test(input)) {
value = getCurrentTimeAsString();
ref?.current?.focusAtEnd();
} else if (/\/datetime$/.test(value)) {
title = getCurrentDateTimeAsString();
} else if (/\/datetime$/.test(input)) {
value = getCurrentDateTimeAsString();
ref?.current?.focusAtEnd();
}
onChangeTitle?.(title);
onChangeTitle?.(value);
},
[ref, onChangeTitle]
);
@@ -257,16 +257,16 @@ const DocumentTitle = React.forwardRef(function _DocumentTitle(
{can.update && !readOnly ? (
<IconWrapper align="center" justify="center" dir={dir}>
<React.Suspense fallback={fallbackIcon}>
<StyledIconPicker
<IconPicker
icon={icon ?? null}
color={color}
size={40}
popoverPosition="bottom-start"
allowDelete={true}
borderOnHover={true}
onChange={handleIconChange}
onOpen={handleOpen}
onClose={handleClose}
allowDelete
borderOnHover
/>
</React.Suspense>
</IconWrapper>
@@ -346,10 +346,6 @@ const Title = styled(ContentEditable)<TitleProps>`
}
`;
const StyledIconPicker = styled(IconPicker)`
${extraArea(8)}
`;
const IconWrapper = styled(Flex)<{ dir?: string }>`
position: absolute;
top: 3px;

View File

@@ -244,7 +244,7 @@ export class IconLibrary {
keywords: "browser web app",
},
collection: {
component: CollectionIcon,
component: (props) => <CollectionIcon expanded {...props} />,
keywords: "collection",
},
coins: {