Skip to content

Commit

Permalink
fix: Add indicator of starred status when viewing a document (#1785)
Browse files Browse the repository at this point in the history
* fix: Add indicator of starred status when viewing a document
closes #461

* fix: Account for shared document
  • Loading branch information
tommoor committed Jan 11, 2021
1 parent 4b85603 commit 7504d43
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 62 deletions.
57 changes: 13 additions & 44 deletions app/components/DocumentListItem.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
// @flow
import { observer } from "mobx-react";
import { StarredIcon, PlusIcon } from "outline-icons";
import { PlusIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link, useHistory } from "react-router-dom";
import styled, { css, withTheme } from "styled-components";
import styled, { css } from "styled-components";
import Document from "models/Document";
import Badge from "components/Badge";
import Button from "components/Button";
import DocumentMeta from "components/DocumentMeta";
import EventBoundary from "components/EventBoundary";
import Flex from "components/Flex";
import Highlight from "components/Highlight";
import StarButton, { AnimatedStar } from "components/Star";
import Tooltip from "components/Tooltip";
import useCurrentUser from "hooks/useCurrentUser";
import DocumentMenu from "menus/DocumentMenu";
Expand Down Expand Up @@ -52,24 +53,6 @@ function DocumentListItem(props: Props) {
context,
} = props;

const handleStar = React.useCallback(
(ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();
document.star();
},
[document]
);

const handleUnstar = React.useCallback(
(ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();
document.unstar();
},
[document]
);

const handleNewFromTemplate = React.useCallback(
(ev: SyntheticEvent<>) => {
ev.preventDefault();
Expand All @@ -90,7 +73,8 @@ function DocumentListItem(props: Props) {

return (
<DocumentLink
menuOpen={menuOpen}
$isStarred={document.isStarred}
$menuOpen={menuOpen}
to={{
pathname: document.url,
state: { title: document.titleWithDefault },
Expand All @@ -103,11 +87,7 @@ function DocumentListItem(props: Props) {
)}
{!document.isDraft && !document.isArchived && !document.isTemplate && (
<Actions>
{document.isStarred ? (
<StyledStar onClick={handleUnstar} solid />
) : (
<StyledStar onClick={handleStar} />
)}
<StarButton document={document} />
</Actions>
)}
{document.isDraft && showDraft && (
Expand Down Expand Up @@ -157,21 +137,6 @@ function DocumentListItem(props: Props) {
);
}

const StyledStar = withTheme(styled(({ solid, theme, ...props }) => (
<StarredIcon color={theme.text} {...props} />
))`
flex-shrink: 0;
opacity: ${(props) => (props.solid ? "1 !important" : 0)};
transition: all 100ms ease-in-out;
&:hover {
transform: scale(1.1);
}
&:active {
transform: scale(0.95);
}
`);

const SecondaryActions = styled(Flex)`
align-items: center;
position: absolute;
Expand All @@ -195,6 +160,10 @@ const DocumentLink = styled(Link)`
opacity: 0;
}
${AnimatedStar} {
opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)};
}
&:hover,
&:active,
&:focus {
Expand All @@ -204,7 +173,7 @@ const DocumentLink = styled(Link)`
opacity: 1;
}
${StyledStar} {
${AnimatedStar} {
opacity: 0.5;
&:hover {
Expand All @@ -214,15 +183,15 @@ const DocumentLink = styled(Link)`
}
${(props) =>
props.menuOpen &&
props.$menuOpen &&
css`
background: ${(props) => props.theme.listItemHoverBackground};
${SecondaryActions} {
opacity: 1;
}
${StyledStar} {
${AnimatedStar} {
opacity: 0.5;
}
`}
Expand Down
10 changes: 5 additions & 5 deletions app/components/NudeButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as React from "react";
import styled from "styled-components";

const Button = styled.button`
width: 24px;
height: 24px;
width: ${(props) => props.size}px;
height: ${(props) => props.size}px;
background: none;
border-radius: 4px;
line-height: 0;
Expand All @@ -14,6 +14,6 @@ const Button = styled.button`
user-select: none;
`;

export default React.forwardRef<any, typeof Button>((props, ref) => (
<Button {...props} ref={ref} />
));
export default React.forwardRef<any, typeof Button>(
({ size = 24, ...props }, ref) => <Button size={size} {...props} ref={ref} />
);
59 changes: 59 additions & 0 deletions app/components/Star.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// @flow
import { StarredIcon } from "outline-icons";
import * as React from "react";
import styled from "styled-components";
import Document from "models/Document";
import NudeButton from "./NudeButton";

type Props = {|
document: Document,
size?: number,
|};

function Star({ size, document, ...rest }: Props) {
const handleClick = React.useCallback(
(ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();

if (document.isStarred) {
document.unstar();
} else {
document.star();
}
},
[document]
);

if (!document) {
return null;
}

return (
<Button onClick={handleClick} size={size} {...rest}>
<AnimatedStar
solid={document.isStarred}
size={size}
color="currentColor"
/>
</Button>
);
}

const Button = styled(NudeButton)`
color: ${(props) => props.theme.text};
`;

export const AnimatedStar = styled(StarredIcon)`
flex-shrink: 0;
transition: all 100ms ease-in-out;
&:hover {
transform: scale(1.1);
}
&:active {
transform: scale(0.95);
}
`;

export default Star;
57 changes: 45 additions & 12 deletions app/scenes/Document/components/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import DocumentMetaWithViews from "components/DocumentMetaWithViews";
import Editor from "components/Editor";
import Flex from "components/Flex";
import HoverPreview from "components/HoverPreview";
import Star, { AnimatedStar } from "components/Star";
import { isMetaKey } from "utils/keyboard";
import { documentHistoryUrl } from "utils/routeHelpers";

Expand Down Expand Up @@ -98,23 +99,35 @@ class DocumentEditor extends React.Component<Props> {
readOnly,
innerRef,
} = this.props;

const { emoji } = parseTitle(title);
const startsWithEmojiAndSpace = !!(emoji && title.startsWith(`${emoji} `));
const normalizedTitle =
!title && readOnly ? document.titleWithDefault : title;

return (
<Flex auto column>
<Title
type="text"
onChange={onChangeTitle}
onKeyDown={this.handleTitleKeyDown}
placeholder={document.placeholder}
value={!title && readOnly ? document.titleWithDefault : title}
style={startsWithEmojiAndSpace ? { marginLeft: "-1.2em" } : undefined}
readOnly={readOnly}
disabled={readOnly}
autoFocus={!title}
maxLength={MAX_TITLE_LENGTH}
/>
{readOnly ? (
<Title
as="div"
$startsWithEmojiAndSpace={startsWithEmojiAndSpace}
$isStarred={document.isStarred}
>
<span>{normalizedTitle}</span>{" "}
{!isShare && <StarButton document={document} size={32} />}
</Title>
) : (
<Title
type="text"
onChange={onChangeTitle}
onKeyDown={this.handleTitleKeyDown}
placeholder={document.placeholder}
value={normalizedTitle}
$startsWithEmojiAndSpace={startsWithEmojiAndSpace}
autoFocus={!title}
maxLength={MAX_TITLE_LENGTH}
/>
)}
<DocumentMetaWithViews
isDraft={isDraft}
document={document}
Expand Down Expand Up @@ -142,11 +155,17 @@ class DocumentEditor extends React.Component<Props> {
}
}

const StarButton = styled(Star)`
position: relative;
top: 4px;
`;

const Title = styled(Textarea)`
z-index: 1;
line-height: 1.25;
margin-top: 1em;
margin-bottom: 0.5em;
margin-left: ${(props) => (props.$startsWithEmojiAndSpace ? "-1.2em" : 0)};
background: ${(props) => props.theme.background};
transition: ${(props) => props.theme.backgroundTransition};
color: ${(props) => props.theme.text};
Expand All @@ -162,6 +181,20 @@ const Title = styled(Textarea)`
color: ${(props) => props.theme.placeholder};
-webkit-text-fill-color: ${(props) => props.theme.placeholder};
}
${AnimatedStar} {
opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)};
}
&:hover {
${AnimatedStar} {
opacity: 0.5;
&:hover {
opacity: 1;
}
}
}
`;

export default DocumentEditor;
4 changes: 3 additions & 1 deletion app/utils/keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const metaDisplay = isMac ? "⌘" : "Ctrl";

export const meta = isMac ? "cmd" : "ctrl";

export function isMetaKey(event: KeyboardEvent | MouseEvent) {
export function isMetaKey(
event: KeyboardEvent | MouseEvent | SyntheticKeyboardEvent<>
) {
return isMac ? event.metaKey : event.ctrlKey;
}

0 comments on commit 7504d43

Please sign in to comment.