Merge branch 'develop' of github.com:outline/outline into develop

This commit is contained in:
Tom Moor
2021-01-11 00:47:48 -08:00
5 changed files with 125 additions and 62 deletions

View File

@@ -1,10 +1,10 @@
// @flow // @flow
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { StarredIcon, PlusIcon } from "outline-icons"; import { PlusIcon } from "outline-icons";
import * as React from "react"; import * as React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link, useHistory } from "react-router-dom"; 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 Document from "models/Document";
import Badge from "components/Badge"; import Badge from "components/Badge";
import Button from "components/Button"; import Button from "components/Button";
@@ -12,6 +12,7 @@ import DocumentMeta from "components/DocumentMeta";
import EventBoundary from "components/EventBoundary"; import EventBoundary from "components/EventBoundary";
import Flex from "components/Flex"; import Flex from "components/Flex";
import Highlight from "components/Highlight"; import Highlight from "components/Highlight";
import StarButton, { AnimatedStar } from "components/Star";
import Tooltip from "components/Tooltip"; import Tooltip from "components/Tooltip";
import useCurrentUser from "hooks/useCurrentUser"; import useCurrentUser from "hooks/useCurrentUser";
import DocumentMenu from "menus/DocumentMenu"; import DocumentMenu from "menus/DocumentMenu";
@@ -52,24 +53,6 @@ function DocumentListItem(props: Props) {
context, context,
} = props; } = 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( const handleNewFromTemplate = React.useCallback(
(ev: SyntheticEvent<>) => { (ev: SyntheticEvent<>) => {
ev.preventDefault(); ev.preventDefault();
@@ -90,7 +73,8 @@ function DocumentListItem(props: Props) {
return ( return (
<DocumentLink <DocumentLink
menuOpen={menuOpen} $isStarred={document.isStarred}
$menuOpen={menuOpen}
to={{ to={{
pathname: document.url, pathname: document.url,
state: { title: document.titleWithDefault }, state: { title: document.titleWithDefault },
@@ -103,11 +87,7 @@ function DocumentListItem(props: Props) {
)} )}
{!document.isDraft && !document.isArchived && !document.isTemplate && ( {!document.isDraft && !document.isArchived && !document.isTemplate && (
<Actions> <Actions>
{document.isStarred ? ( <StarButton document={document} />
<StyledStar onClick={handleUnstar} solid />
) : (
<StyledStar onClick={handleStar} />
)}
</Actions> </Actions>
)} )}
{document.isDraft && showDraft && ( {document.isDraft && showDraft && (
@@ -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)` const SecondaryActions = styled(Flex)`
align-items: center; align-items: center;
position: absolute; position: absolute;
@@ -195,6 +160,10 @@ const DocumentLink = styled(Link)`
opacity: 0; opacity: 0;
} }
${AnimatedStar} {
opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)};
}
&:hover, &:hover,
&:active, &:active,
&:focus { &:focus {
@@ -204,7 +173,7 @@ const DocumentLink = styled(Link)`
opacity: 1; opacity: 1;
} }
${StyledStar} { ${AnimatedStar} {
opacity: 0.5; opacity: 0.5;
&:hover { &:hover {
@@ -214,7 +183,7 @@ const DocumentLink = styled(Link)`
} }
${(props) => ${(props) =>
props.menuOpen && props.$menuOpen &&
css` css`
background: ${(props) => props.theme.listItemHoverBackground}; background: ${(props) => props.theme.listItemHoverBackground};
@@ -222,7 +191,7 @@ const DocumentLink = styled(Link)`
opacity: 1; opacity: 1;
} }
${StyledStar} { ${AnimatedStar} {
opacity: 0.5; opacity: 0.5;
} }
`} `}

View File

@@ -3,8 +3,8 @@ import * as React from "react";
import styled from "styled-components"; import styled from "styled-components";
const Button = styled.button` const Button = styled.button`
width: 24px; width: ${(props) => props.size}px;
height: 24px; height: ${(props) => props.size}px;
background: none; background: none;
border-radius: 4px; border-radius: 4px;
line-height: 0; line-height: 0;
@@ -14,6 +14,6 @@ const Button = styled.button`
user-select: none; user-select: none;
`; `;
export default React.forwardRef<any, typeof Button>((props, ref) => ( export default React.forwardRef<any, typeof Button>(
<Button {...props} ref={ref} /> ({ size = 24, ...props }, ref) => <Button size={size} {...props} ref={ref} />
)); );

59
app/components/Star.js Normal file
View File

@@ -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;

View File

@@ -12,6 +12,7 @@ import DocumentMetaWithViews from "components/DocumentMetaWithViews";
import Editor from "components/Editor"; import Editor from "components/Editor";
import Flex from "components/Flex"; import Flex from "components/Flex";
import HoverPreview from "components/HoverPreview"; import HoverPreview from "components/HoverPreview";
import Star, { AnimatedStar } from "components/Star";
import { isMetaKey } from "utils/keyboard"; import { isMetaKey } from "utils/keyboard";
import { documentHistoryUrl } from "utils/routeHelpers"; import { documentHistoryUrl } from "utils/routeHelpers";
@@ -98,23 +99,35 @@ class DocumentEditor extends React.Component<Props> {
readOnly, readOnly,
innerRef, innerRef,
} = this.props; } = this.props;
const { emoji } = parseTitle(title); const { emoji } = parseTitle(title);
const startsWithEmojiAndSpace = !!(emoji && title.startsWith(`${emoji} `)); const startsWithEmojiAndSpace = !!(emoji && title.startsWith(`${emoji} `));
const normalizedTitle =
!title && readOnly ? document.titleWithDefault : title;
return ( return (
<Flex auto column> <Flex auto column>
<Title {readOnly ? (
type="text" <Title
onChange={onChangeTitle} as="div"
onKeyDown={this.handleTitleKeyDown} $startsWithEmojiAndSpace={startsWithEmojiAndSpace}
placeholder={document.placeholder} $isStarred={document.isStarred}
value={!title && readOnly ? document.titleWithDefault : title} >
style={startsWithEmojiAndSpace ? { marginLeft: "-1.2em" } : undefined} <span>{normalizedTitle}</span>{" "}
readOnly={readOnly} {!isShare && <StarButton document={document} size={32} />}
disabled={readOnly} </Title>
autoFocus={!title} ) : (
maxLength={MAX_TITLE_LENGTH} <Title
/> type="text"
onChange={onChangeTitle}
onKeyDown={this.handleTitleKeyDown}
placeholder={document.placeholder}
value={normalizedTitle}
$startsWithEmojiAndSpace={startsWithEmojiAndSpace}
autoFocus={!title}
maxLength={MAX_TITLE_LENGTH}
/>
)}
<DocumentMetaWithViews <DocumentMetaWithViews
isDraft={isDraft} isDraft={isDraft}
document={document} document={document}
@@ -142,11 +155,17 @@ class DocumentEditor extends React.Component<Props> {
} }
} }
const StarButton = styled(Star)`
position: relative;
top: 4px;
`;
const Title = styled(Textarea)` const Title = styled(Textarea)`
z-index: 1; z-index: 1;
line-height: 1.25; line-height: 1.25;
margin-top: 1em; margin-top: 1em;
margin-bottom: 0.5em; margin-bottom: 0.5em;
margin-left: ${(props) => (props.$startsWithEmojiAndSpace ? "-1.2em" : 0)};
background: ${(props) => props.theme.background}; background: ${(props) => props.theme.background};
transition: ${(props) => props.theme.backgroundTransition}; transition: ${(props) => props.theme.backgroundTransition};
color: ${(props) => props.theme.text}; color: ${(props) => props.theme.text};
@@ -162,6 +181,20 @@ const Title = styled(Textarea)`
color: ${(props) => props.theme.placeholder}; color: ${(props) => props.theme.placeholder};
-webkit-text-fill-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; export default DocumentEditor;

View File

@@ -5,6 +5,8 @@ export const metaDisplay = isMac ? "⌘" : "Ctrl";
export const meta = isMac ? "cmd" : "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; return isMac ? event.metaKey : event.ctrlKey;
} }