Pinned documents (#608)
* Migrations and API for pinned documents * Documentation * Add pin icon * Fin. * v0.2.0 * Remove pin from DocumentPreview, add general menu Add Pinned documents header * Tidy * Fixed: Drafts appearing on collection home
This commit is contained in:
@@ -8,24 +8,27 @@ class DocumentList extends React.Component {
|
||||
props: {
|
||||
documents: Document[],
|
||||
showCollection?: boolean,
|
||||
limit?: number,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { documents, showCollection } = this.props;
|
||||
const { limit, showCollection } = this.props;
|
||||
const documents = limit
|
||||
? this.props.documents.splice(0, limit)
|
||||
: this.props.documents;
|
||||
|
||||
return (
|
||||
<ArrowKeyNavigation
|
||||
mode={ArrowKeyNavigation.mode.VERTICAL}
|
||||
defaultActiveChildIndex={0}
|
||||
>
|
||||
{documents &&
|
||||
documents.map(document => (
|
||||
<DocumentPreview
|
||||
key={document.id}
|
||||
document={document}
|
||||
showCollection={showCollection}
|
||||
/>
|
||||
))}
|
||||
{documents.map(document => (
|
||||
<DocumentPreview
|
||||
key={document.id}
|
||||
document={document}
|
||||
showCollection={showCollection}
|
||||
/>
|
||||
))}
|
||||
</ArrowKeyNavigation>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ import { Link } from 'react-router-dom';
|
||||
import Document from 'models/Document';
|
||||
import styled from 'styled-components';
|
||||
import { color } from 'shared/styles/constants';
|
||||
import Flex from 'shared/components/Flex';
|
||||
import Highlight from 'components/Highlight';
|
||||
import StarredIcon from 'components/Icon/StarredIcon';
|
||||
import PublishingInfo from './components/PublishingInfo';
|
||||
import DocumentMenu from 'menus/DocumentMenu';
|
||||
|
||||
type Props = {
|
||||
document: Document,
|
||||
@@ -19,10 +21,8 @@ type Props = {
|
||||
const StyledStar = styled(({ solid, ...props }) => (
|
||||
<StarredIcon color={solid ? color.black : color.text} {...props} />
|
||||
))`
|
||||
position: absolute;
|
||||
opacity: ${props => (props.solid ? '1 !important' : 0)};
|
||||
transition: all 100ms ease-in-out;
|
||||
margin-left: 2px;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
@@ -32,6 +32,13 @@ const StyledStar = styled(({ solid, ...props }) => (
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDocumentMenu = styled(DocumentMenu)`
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
`;
|
||||
|
||||
const DocumentLink = styled(Link)`
|
||||
display: block;
|
||||
margin: 0 -16px;
|
||||
@@ -41,6 +48,11 @@ const DocumentLink = styled(Link)`
|
||||
max-height: 50vh;
|
||||
min-width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
${StyledDocumentMenu} {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
@@ -49,7 +61,7 @@ const DocumentLink = styled(Link)`
|
||||
border: 2px solid ${color.smoke};
|
||||
outline: none;
|
||||
|
||||
${StyledStar} {
|
||||
${StyledStar}, ${StyledDocumentMenu} {
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
@@ -61,11 +73,19 @@ const DocumentLink = styled(Link)`
|
||||
&:focus {
|
||||
border: 2px solid ${color.slateDark};
|
||||
}
|
||||
`;
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
const Heading = styled.h3`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.25em;
|
||||
`;
|
||||
|
||||
const Actions = styled(Flex)`
|
||||
margin-left: 4px;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
@observer
|
||||
@@ -95,19 +115,19 @@ class DocumentPreview extends Component {
|
||||
|
||||
return (
|
||||
<DocumentLink to={document.url} innerRef={innerRef} {...rest}>
|
||||
<h3>
|
||||
<Heading>
|
||||
<Highlight text={document.title} highlight={highlight} />
|
||||
{document.publishedAt &&
|
||||
(document.starred ? (
|
||||
<span onClick={this.unstar}>
|
||||
<StyledStar solid />
|
||||
</span>
|
||||
) : (
|
||||
<span onClick={this.star}>
|
||||
<StyledStar />
|
||||
</span>
|
||||
))}
|
||||
</h3>
|
||||
{document.publishedAt && (
|
||||
<Actions>
|
||||
{document.starred ? (
|
||||
<StyledStar onClick={this.unstar} solid />
|
||||
) : (
|
||||
<StyledStar onClick={this.star} />
|
||||
)}
|
||||
</Actions>
|
||||
)}
|
||||
<StyledDocumentMenu document={document} />
|
||||
</Heading>
|
||||
<PublishingInfo
|
||||
document={document}
|
||||
collection={showCollection ? document.collection : undefined}
|
||||
|
||||
@@ -14,6 +14,7 @@ type Props = {
|
||||
onOpen?: () => void,
|
||||
onClose?: () => void,
|
||||
children?: React.Element<*>,
|
||||
className?: string,
|
||||
style?: Object,
|
||||
};
|
||||
|
||||
@@ -23,10 +24,9 @@ class DropdownMenu extends Component {
|
||||
@observable top: number;
|
||||
@observable right: number;
|
||||
|
||||
handleOpen = (openPortal: SyntheticEvent => void) => {
|
||||
handleOpen = (openPortal: SyntheticEvent => *) => {
|
||||
return (ev: SyntheticMouseEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const currentTarget = ev.currentTarget;
|
||||
invariant(document.body, 'why you not here');
|
||||
|
||||
@@ -41,30 +41,34 @@ class DropdownMenu extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, label, children } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={className}>
|
||||
<PortalWithState
|
||||
onOpen={this.props.onOpen}
|
||||
onClose={this.props.onClose}
|
||||
closeOnEsc
|
||||
closeOnOutsideClick
|
||||
closeOnEsc
|
||||
>
|
||||
{({ closePortal, openPortal, portal }) => [
|
||||
<Label onClick={this.handleOpen(openPortal)} key="label">
|
||||
{this.props.label}
|
||||
</Label>,
|
||||
portal(
|
||||
<Menu
|
||||
key="menu"
|
||||
onClick={closePortal}
|
||||
style={this.props.style}
|
||||
top={this.top}
|
||||
right={this.right}
|
||||
>
|
||||
{this.props.children}
|
||||
</Menu>
|
||||
),
|
||||
]}
|
||||
{({ closePortal, openPortal, portal }) => (
|
||||
<React.Fragment>
|
||||
<Label onClick={this.handleOpen(openPortal)}>{label}</Label>
|
||||
{portal(
|
||||
<Menu
|
||||
onClick={ev => {
|
||||
ev.stopPropagation();
|
||||
closePortal();
|
||||
}}
|
||||
style={this.props.style}
|
||||
top={this.top}
|
||||
right={this.right}
|
||||
>
|
||||
{children}
|
||||
</Menu>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</PortalWithState>
|
||||
</div>
|
||||
);
|
||||
|
||||
12
app/components/Icon/PinIcon.js
Normal file
12
app/components/Icon/PinIcon.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import Icon from './Icon';
|
||||
import type { Props } from './Icon';
|
||||
|
||||
export default function PinIcon(props: Props) {
|
||||
return (
|
||||
<Icon {...props}>
|
||||
<path d="M5.8355996,10.9120278 C5.68780303,10.7642312 5.59344653,10.5714155 5.56742853,10.3640252 C5.50276659,9.84860302 5.86817998,9.37835193 6.38360216,9.31369 C7.5908221,9.16222691 8.66384332,9.11386012 9.60266432,9.16857768 L13.3590257,6.76661181 C13.3232787,6.36756241 13.3081075,5.93429416 13.3135122,5.46680705 C13.3163525,5.22112701 13.4152107,4.98631045 13.5889444,4.81257683 C13.9562711,4.4452501 14.5518254,4.4452501 14.9191521,4.81257683 L19.1874471,9.08089577 C19.3611825,9.25463123 19.4600417,9.48945036 19.4628817,9.735133 C19.4688865,10.2545814 19.0526582,10.6805453 18.5332098,10.6865501 C18.0657301,10.6919287 17.6324685,10.6767575 17.2334248,10.641011 L14.831459,14.3973723 C14.8861764,15.3361909 14.83781,16.4092091 14.6863598,17.6164268 C14.6603417,17.823818 14.5659848,18.0166347 14.4181875,18.164432 C14.0508706,18.5317489 13.4553322,18.5317489 13.0880152,18.164432 L10.1268984,15.2033198 L6.41184151,18.9183767 C6.04452202,19.2856962 5.44897945,19.2856962 5.08165995,18.9183767 C4.71434046,18.5510572 4.71434046,17.9555146 5.08165995,17.5881951 L8.7967158,13.8731393 L5.8355996,10.9120278 Z" />
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user