Collections got descriptions now

This commit is contained in:
Tom Moor
2018-11-18 01:13:28 -08:00
parent 3718a9609d
commit 919bca6769
7 changed files with 110 additions and 48 deletions

View File

@@ -0,0 +1,38 @@
// @flow
import * as React from 'react';
import styled from 'styled-components';
import RichMarkdownEditor from 'rich-markdown-editor';
import { LabelText, Outline } from 'components/Input';
type Props = {
label: string,
minHeight?: number,
maxHeight?: number,
};
export default function InputRich({
label,
minHeight,
maxHeight,
...rest
}: Props) {
return (
<React.Fragment>
<LabelText>{label}</LabelText>
<StyledOutline maxHeight={maxHeight} minHeight={minHeight}>
<RichMarkdownEditor {...rest} />
</StyledOutline>
</React.Fragment>
);
}
const StyledOutline = styled(Outline)`
padding: 8px 12px;
min-height: ${({ minHeight }) => (minHeight ? `${minHeight}px` : '0')};
max-height: ${({ maxHeight }) => (maxHeight ? `${maxHeight}px` : 'auto')};
overflow: scroll;
> * {
display: block;
}
`;

View File

@@ -12,10 +12,9 @@ import type { NavigationNode } from 'types';
class Collection extends BaseModel {
isSaving: boolean = false;
ui: UiStore;
data: Object;
createdAt: string;
description: ?string;
description: string;
id: string;
name: string;
color: string;
@@ -24,15 +23,6 @@ class Collection extends BaseModel {
updatedAt: string;
url: string;
/* Computed */
@computed
get entryUrl(): string {
return this.type === 'atlas' && this.documents.length > 0
? this.documents[0].url
: this.url;
}
@computed
get isEmpty(): boolean {
return this.documents.length === 0;
@@ -66,8 +56,6 @@ class Collection extends BaseModel {
travelDocuments(this.documents);
}
/* Actions */
@action
fetch = async () => {
try {
@@ -138,7 +126,6 @@ class Collection extends BaseModel {
@action
updateData(data: Object = {}) {
this.data = data;
extendObservable(this, data);
}

View File

@@ -5,6 +5,7 @@ import { observer, inject } from 'mobx-react';
import { withRouter, Link } from 'react-router-dom';
import styled from 'styled-components';
import { CollectionIcon, NewDocumentIcon, PinIcon } from 'outline-icons';
import RichMarkdownEditor from 'rich-markdown-editor';
import { newDocumentUrl } from 'utils/routeHelpers';
import CollectionsStore from 'stores/CollectionsStore';
@@ -157,6 +158,13 @@ class CollectionScene extends React.Component<Props> {
/>{' '}
{this.collection.name}
</Heading>
{this.collection.description && (
<RichMarkdownEditor
key={this.collection.description}
defaultValue={this.collection.description}
readOnly
/>
)}
{hasPinnedDocuments && (
<React.Fragment>

View File

@@ -3,8 +3,9 @@ import * as React from 'react';
import { withRouter } from 'react-router-dom';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import Button from 'components/Button';
import Input from 'components/Input';
import InputRich from 'components/InputRich';
import Button from 'components/Button';
import Flex from 'shared/components/Flex';
import HelpText from 'components/HelpText';
import ColorPicker from 'components/ColorPicker';
@@ -19,18 +20,24 @@ type Props = {
@observer
class CollectionEdit extends React.Component<Props> {
@observable name: string;
@observable description: string = '';
@observable color: string = '';
@observable isSaving: boolean;
componentWillMount() {
this.name = this.props.collection.name;
this.description = this.props.collection.description;
}
handleSubmit = async (ev: SyntheticEvent<*>) => {
ev.preventDefault();
this.isSaving = true;
this.props.collection.updateData({ name: this.name, color: this.color });
this.props.collection.updateData({
name: this.name,
description: this.description,
color: this.color,
});
const success = await this.props.collection.save();
if (success) {
@@ -40,6 +47,10 @@ class CollectionEdit extends React.Component<Props> {
this.isSaving = false;
};
handleDescriptionChange = getValue => {
this.description = getValue();
};
handleNameChange = (ev: SyntheticInputEvent<*>) => {
this.name = ev.target.value;
};
@@ -53,8 +64,8 @@ class CollectionEdit extends React.Component<Props> {
<Flex column>
<form onSubmit={this.handleSubmit}>
<HelpText>
You can edit a collections name at any time, however doing so might
confuse your team mates.
You can edit a collections details at any time, however doing so
might confuse your team mates.
</HelpText>
<Input
type="text"
@@ -64,6 +75,14 @@ class CollectionEdit extends React.Component<Props> {
required
autoFocus
/>
<InputRich
label="Description"
onChange={this.handleDescriptionChange}
defaultValue={this.description || ''}
placeholder="More details about this collection…"
minHeight={68}
maxHeight={200}
/>
<ColorPicker
onSelect={this.handleColor}
value={this.props.collection.color}

View File

@@ -1,9 +1,10 @@
// @flow
import * as React from 'react';
import styled from 'styled-components';
import { Block, Change, Node, Mark, Text } from 'slate';
import RichMarkdownEditor, { Placeholder, schema } from 'rich-markdown-editor';
import { Text } from 'slate';
import RichMarkdownEditor, { Placeholder } from 'rich-markdown-editor';
import ClickablePadding from 'components/ClickablePadding';
import schema from '../schema';
type Props = {
titlePlaceholder?: string,
@@ -12,33 +13,6 @@ type Props = {
readOnly: boolean,
};
// add rules to the schema to ensure the first node is a heading
schema.document.nodes.unshift({ types: ['heading1'], min: 1, max: 1 });
schema.document.normalize = (
change: Change,
reason: string,
{
node,
child,
mark,
index,
}: { node: Node, mark?: Mark, child: Node, index: number }
) => {
switch (reason) {
case 'child_type_invalid': {
return change.setNodeByKey(
child.key,
index === 0 ? 'heading1' : 'paragraph'
);
}
case 'child_required': {
const block = Block.create(index === 0 ? 'heading1' : 'paragraph');
return change.insertNodeByKey(node.key, index, block);
}
default:
}
};
class Editor extends React.Component<Props> {
editor: *;

View File

@@ -0,0 +1,35 @@
// @flow
import { cloneDeep } from 'lodash';
import { Block, Change, Node, Mark } from 'slate';
import { schema as originalSchema } from 'rich-markdown-editor';
const schema = cloneDeep(originalSchema);
// add rules to the schema to ensure the first node is a heading
schema.document.nodes.unshift({ types: ['heading1'], min: 1, max: 1 });
schema.document.normalize = (
change: Change,
reason: string,
{
node,
child,
mark,
index,
}: { node: Node, mark?: Mark, child: Node, index: number }
) => {
switch (reason) {
case 'child_type_invalid': {
return change.setNodeByKey(
child.key,
index === 0 ? 'heading1' : 'paragraph'
);
}
case 'child_required': {
const block = Block.create(index === 0 ? 'heading1' : 'paragraph');
return change.insertNodeByKey(node.key, index, block);
}
default:
}
};
export default schema;

View File

@@ -77,7 +77,7 @@ router.post('collections.exportAll', auth(), async ctx => {
});
router.post('collections.update', auth(), async ctx => {
const { id, name, color } = ctx.body;
const { id, name, description, color } = ctx.body;
ctx.assertPresent(name, 'name is required');
if (color)
ctx.assertHexColor(color, 'Invalid hex value (please use format #FFFFFF)');
@@ -86,6 +86,7 @@ router.post('collections.update', auth(), async ctx => {
authorize(ctx.state.user, 'update', collection);
collection.name = name;
collection.description = description;
collection.color = color;
await collection.save();