Merge pull request #284 from netlify/entry-editor-ui

improve entry editor UI
This commit is contained in:
Shawn Erquhart 2017-03-16 12:07:12 -04:00 committed by GitHub
commit f23229bcb6
8 changed files with 97 additions and 80 deletions

View File

@ -1,3 +1,5 @@
@import "../UI/theme";
.control { .control {
color: #7c8382; color: #7c8382;
position: relative; position: relative;
@ -9,12 +11,13 @@
font-family: monospace; font-family: monospace;
display: block; display: block;
width: 100%; width: 100%;
padding: 12px 0 10px 0; padding: 12px;
margin: 0; margin: 0;
border: none; border: none;
border-radius: var(--borderRadius);
outline: 0; outline: 0;
box-shadow: none; box-shadow: none;
background: 0 0; background-color: var(--controlBGColor);
font-size: 18px; font-size: 18px;
color: #7c8382; color: #7c8382;
} }
@ -22,8 +25,8 @@
.label { .label {
display: block; display: block;
color: #AAB0AF; color: var(--controlLabelColor);
font-size: 12px; font-size: 14px;
} }
.labelWithError { .labelWithError {
@ -37,31 +40,3 @@
color: #FF706F; color: #FF706F;
margin-bottom: 5px; margin-bottom: 5px;
} }
.widget {
border-bottom: 1px solid #e8eae8;
position: relative;
&:after {
content: '';
position: absolute;
left: 42px;
bottom: -7px;
width: 12px;
height: 12px;
background-color: #f2f5f4;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
z-index: 1;
border-right: 1px solid #e8eae8;
border-bottom: 1px solid #e8eae8;
}
&:last-child {
border-bottom: none;
}
&:last-child:after {
display: none;
}
}

View File

@ -5,6 +5,7 @@
overflow: auto; overflow: auto;
padding: 0 20px; padding: 0 20px;
border-right: 1px solid var(--defaultColorLight); border-right: 1px solid var(--defaultColorLight);
background-color: color(#f2f5f4 lightness(90%));
} }
.previewPane { .previewPane {

View File

@ -14,6 +14,8 @@
--textFieldBorderColor: #e7e7e7; --textFieldBorderColor: #e7e7e7;
--highlightFGColor: #fff; --highlightFGColor: #fff;
--highlightBGColor: #3ab7a5; --highlightBGColor: #3ab7a5;
--controlLabelColor: #272e30;
--controlBGColor: #fff;
} }
.base { .base {

View File

@ -1,55 +1,66 @@
@import "../UI/theme";
:global(.list-item-dragging) { :global(.list-item-dragging) {
opacity: 0.5; opacity: 0.5;
} }
.addButton { .addButton {
display: block; display: flex;
justify-content: center;
align-items: center;
cursor: pointer; cursor: pointer;
margin: 20px 0; margin-top: 20px;
border: none; padding: 8px;
background: transparent; border: 0;
&::before { border-radius: var(--borderRadius);
content: "+"; background-color: var(--controlBGColor);
display: inline-block;
margin-right: 5px;
width: 15px;
height: 15px;
border: 1px solid #444;
border-radius: 100%;
background: transparent;
line-height: 13px;
} }
.addButtonIcon {
font-size: 18px;
}
.addButtonText {
margin-left: 4px;
} }
.removeButton { .removeButton {
position: absolute; position: absolute;
top: 5px; top: 2px;
right: 5px; right: 2px;
display: inline-block;
cursor: pointer; cursor: pointer;
border: none; display: flex;
background: transparent; align-items: center;
width: 15px; padding: 0 0 2px 2px;
height: 15px; border: 0;
border: 1px solid #444; background: none;
border-radius: 100%; z-index: 1;
line-height: 13px;
} }
.toggleButton { .toggleButton {
position: absolute; position: absolute;
top: 5px; top: 0;
left: 5px; left: 0;
cursor: pointer;
display: flex;
align-items: center;
height: 28px;
border: none;
border-radius: var(--borderRadius) 0 0 var(--borderRadius);
background: rgba(0,0,0,0.1);
} }
.item { .item {
position: relative; position: relative;
padding-left: 20px; padding-left: 24px;
cursor: move; cursor: move;
} }
.objectLabel { .objectLabel {
border: 1px solid #e8eae8; border: 2px solid rgba(0,0,0,0.1);
border-top-width: 28px;
border-radius: var(--borderRadius);
border-top-left-radius: 0;
margin-bottom: 20px; margin-bottom: 20px;
padding: 20px; padding: 20px;
display: none; display: none;
@ -57,6 +68,8 @@
.objectControl { .objectControl {
display: block; display: block;
border-top: 28px solid rgba(0,0,0,0.1);
border-top-left-radius: 0;
} }
.expanded { .expanded {
@ -70,3 +83,11 @@
display: none; display: none;
} }
} }
.dragIcon {
position: absolute;
top: 2px;
display: block;
width: 100%;
text-align: center;
}

View File

@ -1,6 +1,7 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { List, Map, fromJS } from 'immutable'; import { List, Map, fromJS } from 'immutable';
import { sortable } from 'react-sortable'; import { sortable } from 'react-sortable';
import FontIcon from 'react-toolbox/lib/font_icon';
import ObjectControl from './ObjectControl'; import ObjectControl from './ObjectControl';
import styles from './ListControl.css'; import styles from './ListControl.css';
@ -143,22 +144,24 @@ export default class ListControl extends Component {
outline="list" outline="list"
> >
<div className={classNames.join(' ')}> <div className={classNames.join(' ')}>
<button className={styles.toggleButton} onClick={this.handleToggle(index)}>
<FontIcon value={collapsed ? 'expand_more' : 'expand_less'} />
</button>
<FontIcon value="drag_handle" className={styles.dragIcon} />
<button className={styles.removeButton} onClick={this.handleRemove(index)}>
<FontIcon value="close" />
</button>
<div className={styles.objectLabel}>{this.objectLabel(item)}</div> <div className={styles.objectLabel}>{this.objectLabel(item)}</div>
<div className={styles.objectControl}>
<ObjectControl <ObjectControl
value={item} value={item}
field={field} field={field}
className={styles.objectControl}
onChange={this.handleChangeFor(index)} onChange={this.handleChangeFor(index)}
getAsset={getAsset} getAsset={getAsset}
onAddAsset={onAddAsset} onAddAsset={onAddAsset}
onRemoveAsset={onRemoveAsset} onRemoveAsset={onRemoveAsset}
/> />
</div> </div>
<button className={styles.toggleButton} onClick={this.handleToggle(index)}>
{collapsed ? '+' : '-'}
</button>
<button className={styles.removeButton} onClick={this.handleRemove(index)}>x</button>
</div>
</SortableListItem>); </SortableListItem>);
} }
@ -166,7 +169,10 @@ export default class ListControl extends Component {
const { value, forID } = this.props; const { value, forID } = this.props;
return (<div id={forID}> return (<div id={forID}>
{value && value.map((item, index) => this.renderItem(item, index))} {value && value.map((item, index) => this.renderItem(item, index))}
<div><button className={styles.addButton} onClick={this.handleAdd}>new</button></div> <button className={styles.addButton} onClick={this.handleAdd}>
<FontIcon value="add" className={styles.addButtonIcon} />
<span className={styles.addButtonText}>new</span>
</button>
</div>); </div>);
} }

View File

@ -1,3 +1,5 @@
@import "../../../UI/theme";
.editor { .editor {
position: relative; position: relative;
& h1, & h2, & h3 { & h1, & h2, & h3 {
@ -57,6 +59,9 @@
:global { :global {
& .ProseMirror { & .ProseMirror {
position: relative; position: relative;
background-color: var(--controlBGColor);
padding: 12px;
border-radius: var(--borderRadius);
} }
& .ProseMirror-content { & .ProseMirror-content {

View File

@ -1,6 +1,9 @@
@import "../UI/theme";
.root { .root {
position: relative; position: relative;
border: 1px solid #e8eae8; border: 2px solid rgba(0,0,0,0.1);
border-radius: var(--borderRadius);
margin-bottom: 20px; margin-bottom: 20px;
padding: 20px; padding: 0 20px 12px;
} }

View File

@ -8,10 +8,12 @@ export default class ObjectControl extends Component {
static propTypes = { static propTypes = {
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onAddAsset: PropTypes.func.isRequired, onAddAsset: PropTypes.func.isRequired,
onRemoveAsset: PropTypes.func.isRequired,
getAsset: PropTypes.func.isRequired, getAsset: PropTypes.func.isRequired,
value: PropTypes.node, value: PropTypes.node,
field: PropTypes.object, field: PropTypes.object,
forID: PropTypes.string.isRequired, forID: PropTypes.string.isRequired,
className: PropTypes.string,
}; };
controlFor(field) { controlFor(field) {
@ -21,9 +23,10 @@ export default class ObjectControl extends Component {
return (<div className={controlStyles.widget} key={field.get('name')}> return (<div className={controlStyles.widget} key={field.get('name')}>
<div className={controlStyles.control} key={field.get('name')}> <div className={controlStyles.control} key={field.get('name')}>
<label className={controlStyles.label}>{field.get('label')}</label> <label className={controlStyles.label} htmlFor={field.get('name')}>{field.get('label')}</label>
{ {
React.createElement(widget.control, { React.createElement(widget.control, {
id: field.get('name'),
field, field,
value: fieldValue, value: fieldValue,
onChange: (val, metadata) => { onChange: (val, metadata) => {
@ -42,10 +45,11 @@ export default class ObjectControl extends Component {
const { field, forID } = this.props; const { field, forID } = this.props;
const multiFields = field.get('fields'); const multiFields = field.get('fields');
const singleField = field.get('field'); const singleField = field.get('field');
const className = this.props.className || '';
if (multiFields) { if (multiFields) {
return (<div id={forID} className={styles.root}> return (<div id={forID} className={`${ className } ${ styles.root }`}>
{multiFields.map(field => this.controlFor(field))} {multiFields.map(f => this.controlFor(f))}
</div>); </div>);
} else if (singleField) { } else if (singleField) {
return this.controlFor(singleField); return this.controlFor(singleField);