From 7504d43452fb88e60235e9062e90a36bd35ce538 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 10 Jan 2021 23:13:58 -0800 Subject: [PATCH] fix: Add indicator of starred status when viewing a document (#1785) * fix: Add indicator of starred status when viewing a document closes #461 * fix: Account for shared document --- app/components/DocumentListItem.js | 57 ++++++----------------- app/components/NudeButton.js | 10 ++-- app/components/Star.js | 59 ++++++++++++++++++++++++ app/scenes/Document/components/Editor.js | 57 ++++++++++++++++++----- app/utils/keyboard.js | 4 +- 5 files changed, 125 insertions(+), 62 deletions(-) create mode 100644 app/components/Star.js diff --git a/app/components/DocumentListItem.js b/app/components/DocumentListItem.js index f67b078b3..d292e99b0 100644 --- a/app/components/DocumentListItem.js +++ b/app/components/DocumentListItem.js @@ -1,10 +1,10 @@ // @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"; @@ -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 StarButton, { AnimatedStar } from "components/Star"; import Tooltip from "components/Tooltip"; import useCurrentUser from "hooks/useCurrentUser"; import DocumentMenu from "menus/DocumentMenu"; @@ -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(); @@ -90,7 +73,8 @@ function DocumentListItem(props: Props) { return ( - {document.isStarred ? ( - - ) : ( - - )} + )} {document.isDraft && showDraft && ( @@ -157,21 +137,6 @@ function DocumentListItem(props: Props) { ); } -const StyledStar = withTheme(styled(({ solid, theme, ...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; @@ -195,6 +160,10 @@ const DocumentLink = styled(Link)` opacity: 0; } + ${AnimatedStar} { + opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)}; + } + &:hover, &:active, &:focus { @@ -204,7 +173,7 @@ const DocumentLink = styled(Link)` opacity: 1; } - ${StyledStar} { + ${AnimatedStar} { opacity: 0.5; &:hover { @@ -214,7 +183,7 @@ const DocumentLink = styled(Link)` } ${(props) => - props.menuOpen && + props.$menuOpen && css` background: ${(props) => props.theme.listItemHoverBackground}; @@ -222,7 +191,7 @@ const DocumentLink = styled(Link)` opacity: 1; } - ${StyledStar} { + ${AnimatedStar} { opacity: 0.5; } `} diff --git a/app/components/NudeButton.js b/app/components/NudeButton.js index 778f47daf..f42fcdf97 100644 --- a/app/components/NudeButton.js +++ b/app/components/NudeButton.js @@ -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; @@ -14,6 +14,6 @@ const Button = styled.button` user-select: none; `; -export default React.forwardRef((props, ref) => ( - + ); +} + +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; diff --git a/app/scenes/Document/components/Editor.js b/app/scenes/Document/components/Editor.js index e06dc0918..b80810557 100644 --- a/app/scenes/Document/components/Editor.js +++ b/app/scenes/Document/components/Editor.js @@ -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"; @@ -98,23 +99,35 @@ class DocumentEditor extends React.Component { readOnly, innerRef, } = this.props; + const { emoji } = parseTitle(title); const startsWithEmojiAndSpace = !!(emoji && title.startsWith(`${emoji} `)); + const normalizedTitle = + !title && readOnly ? document.titleWithDefault : title; return ( - + {readOnly ? ( + <Title + as="div" + $startsWithEmojiAndSpace={startsWithEmojiAndSpace} + $isStarred={document.isStarred} + > + <span>{normalizedTitle}</span>{" "} + {!isShare && <StarButton document={document} size={32} />} + + ) : ( + + )} <DocumentMetaWithViews isDraft={isDraft} document={document} @@ -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}; @@ -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; diff --git a/app/utils/keyboard.js b/app/utils/keyboard.js index 43291f537..ff4b3b98e 100644 --- a/app/utils/keyboard.js +++ b/app/utils/keyboard.js @@ -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; }