Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ report.json
.compiled-sources/
src/app/compiled-less/
expansions.yml
.nvmrc
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@
"get-object-path": "azer/get-object-path#74eb42de0cfd02c14ffdd18552f295aba723d394",
"hadron-action": "^0.0.4",
"hadron-auto-update-manager": "^0.0.12",
"hadron-compile-cache": "^0.1.0",
"hadron-compile-cache": "^0.2.0",
"hadron-component-registry": "^0.4.0",
"hadron-document": "^0.13.0",
"hadron-document": "^0.14.0",
"hadron-ipc": "^0.0.7",
"hadron-module-cache": "^0.0.3",
"hadron-package-manager": "0.1.0",
Expand Down
14 changes: 14 additions & 0 deletions src/help/entries/cloning-a-single-document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Cloning a Single Document
tags:
- crud
related:
- inserting-a-single-document
section: CRUD
---

A single document can be cloned by clicking on the clone icon
<i class='fa fa-clone' aria-hidden='true'></i> on the right
side of the document in the document list. The Insert Document
modal will appear with all elements in the document cloned with
the exception of the `_id` element.
12 changes: 12 additions & 0 deletions src/help/entries/deleting-a-single-document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Deleting a Single Document
tags:
- crud
section: CRUD
---

A single document can be deleted by clicking on the delete icon
<i class='fa fa-trash-o' aria-hidden='true'></i> on the right
side of the document in the document list. Clicking on this button puts
the document into a pending delete mode, but the delete command will
not be sent to the server until the user confirms the edits by clicking 'Delete'.
53 changes: 53 additions & 0 deletions src/help/entries/editing-a-single-document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: Editing a Single Document
tags:
- crud
section: CRUD
---

A single document can be edited by clicking on the edit icon
<i class='fa fa-pencil' aria-hidden='true'></i> on the right
side of the document in the document list. Clicking on this button puts
the document into edit mode, but changes will not be sent to the server
until the user confirms the edits by clicking 'Update'.

In edit mode, the document panel behaves similar to the CSS editor in
most modern web browers' development tools.

### Editing an Element

Clicking on an element key or value allows the user to change the key name
or the value of the element. The element's type can also be changed by
selecting a new type from the dropdown on the right side. Only types that
the value can currently be cast to will be visible in the list. Duplicate
key names will cause the key field to be highlighted in red.

### Adding an Element

A new element can be added to the document or any embedded document by
either clicking on the right side of the element or tabbing off the last
element's value field if the element is the last element in the document
or sub document. Clicking to the right of an element will also remove any
subsequent extra empty elements.

### Deleting an Element

An element can be deleted by clicking on the <i class='fa fa-times-circle' aria-hidden='true'></i>
icon to the left of the element's line number.

### Reverting a Change

A change to an element can be reverted by clicking the revert icon
<i class='fa fa-rotate-left' aria-hidden='true'></i>
to the left of the element's line number.

### Persisting Changes

The changes to a document may be persisted by clicking on the 'Update'
button in the footer of the document panel. Clicking this button will
execute a `$findAndModify` on the server and update the document in the
list.

### Canceling Changes

To exit edit mode and cancel all pending changes, click the 'Cancel' button.
13 changes: 13 additions & 0 deletions src/help/entries/inserting-a-single-document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
title: Inserting a Single Document
tags:
- crud
related:
- editing-a-single-document
section: CRUD
---

A single document can be inserted by clicking on the 'Insert' button at
the top of the document list. An insert modal will open and the user
may edit the new document using the same behaviour provided by document
editing.
39 changes: 14 additions & 25 deletions src/internal-packages/crud/lib/component/document-actions.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
'use strict';

const React = require('react');
const app = require('ampersand-app');
const IconButton = require('./icon-button');

/**
* The feature flag.
*/
const FEATURE = 'singleDocumentCrud';

/**
* Component for actions on the document.
*/
Expand All @@ -29,26 +23,21 @@ class DocumentActions extends React.Component {
* @returns {Component} The actions component.
*/
render() {
if (app.isFeatureEnabled(FEATURE)) {
return (
<div className='document-actions'>
<IconButton
title='Edit Document'
iconClassName='fa fa-pencil'
clickHandler={this.props.edit} />
<IconButton
title='Delete Document'
iconClassName='fa fa-trash-o'
clickHandler={this.props.remove} />
<IconButton
title='Clone Document'
iconClassName='fa fa-clone'
clickHandler={this.props.clone} />
</div>
);
}
return (
<div className='document-actions'></div>
<div className='document-actions'>
<IconButton
title='Edit Document'
iconClassName='fa fa-pencil'
clickHandler={this.props.edit} />
<IconButton
title='Delete Document'
iconClassName='fa fa-trash-o'
clickHandler={this.props.remove} />
<IconButton
title='Clone Document'
iconClassName='fa fa-clone'
clickHandler={this.props.clone} />
</div>
);
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/internal-packages/crud/lib/component/document.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

const _ = require('lodash');
const app = require('ampersand-app');
const React = require('react');
const Reflux = require('reflux');
Expand Down Expand Up @@ -270,14 +269,15 @@ class Document extends React.Component {
* @returns {Array} The editable elements.
*/
editableElements() {
var components = _.map(this.state.doc.elements, (element) => {
return (
<EditableElement key={element.uuid} element={element} />
);
});
var components = [];
for (let element of this.state.doc.elements) {
components.push(<EditableElement key={element.uuid} element={element} />)
}
// Add the hotspot to the end. In the case of insert, we need to guard against
// No elements being present.
var lastComponent = components[components.length - 1];
var lastElement = lastComponent ? lastComponent.props.element : null;
components.push(<Hotspot key='hotspot' doc={this.state.doc} element={lastElement} />);
var lastElement = lastComponent ? lastComponent.props.element : this.state.doc;
components.push(<Hotspot key='hotspot' element={lastElement} />);
return components;
}

Expand Down
17 changes: 9 additions & 8 deletions src/internal-packages/crud/lib/component/editable-element.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,10 @@ class EditableElement extends React.Component {
<li className={this.style()}>
<div className='line-number'></div>
{this.renderAction()}
<EditableKey element={this.element} insertIndex={this.props.insertIndex} />
<EditableKey element={this.element} index={this.props.index} />
:
{this.renderValue()}
<Hotspot key='hotspot' element={this.element} />
<Types element={this.element} />
</li>
);
Expand Down Expand Up @@ -149,7 +150,7 @@ class EditableElement extends React.Component {
<div className='line-number' onClick={this.toggleExpandable.bind(this)}></div>
{this.renderAction()}
<div className={CARET} onClick={this.toggleExpandable.bind(this)}></div>
<EditableKey element={this.element} />
<EditableKey element={this.element} index={this.props.index} />
:
<div className={LABEL_CLASS} onClick={this.toggleExpandable.bind(this)}>
{this.element.currentType}
Expand All @@ -168,12 +169,12 @@ class EditableElement extends React.Component {
* @returns {Array} The components.
*/
elementComponents() {
var components = _.map(this.element.elements, (element) => {
return (<EditableElement key={element.uuid} element={element} />);
});
// var lastComponent = components[components.length - 1];
// var lastElement = lastComponent ? lastComponent.props.element : null;
// components.push(<Hotspot key='hotspot' doc={this.element} element={lastElement} />);
var components = [];
var index = 0;
for (let element of this.element.elements) {
components.push(<EditableElement key={element.uuid} element={element} index={index} />);
index++;
}
return components;
}

Expand Down
82 changes: 66 additions & 16 deletions src/internal-packages/crud/lib/component/editable-key.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const React = require('react');
const inputSize = require('./utils').inputSize;

/**
* The editing class constant.
Expand All @@ -17,6 +18,16 @@ const DUPLICATE = 'duplicate';
*/
const KEY_CLASS = 'editable-key';

/**
* Escape key code.
*/
const ESC = 27;

/**
* Colon key code.
*/
const COLON = 186;

/**
* General editable key component.
*/
Expand All @@ -38,15 +49,8 @@ class EditableKey extends React.Component {
* to the value field.
*/
componentDidMount() {
if (this.element.isAdded()) {
if (this.props.insertIndex) {
// Focus for inserting new documents.
if (this.props.insertIndex === 1 && this.element.currentKey === '') {
this._node.focus();
}
} else if (!this.isEditable() && this._node) {
this._node.focus();
}
if (this.isAutoFocusable()) {
this._node.focus();
}
}

Expand All @@ -61,16 +65,25 @@ class EditableKey extends React.Component {
type='text'
className={this.style()}
ref={(c) => this._node = c}
size={this.element.currentKey.length}
size={inputSize(this.renderValue())}
tabIndex={this.isEditable() ? 0 : -1}
onBlur={this.handleBlur.bind(this)}
onFocus={this.handleFocus.bind(this)}
onChange={this.handleChange.bind(this)}
onKeyDown={this.handleKeyDown.bind(this)}
value={this.element.currentKey}
onKeyUp={this.handleKeyUp.bind(this)}
value={this.renderValue()}
title={this.renderTitle()} />
);
}

/**
* Render the value of the key.
*/
renderValue() {
return this.element.parent.currentType === 'Array' ? this.props.index : this.element.currentKey;
}

/**
* Render the title.
*
Expand All @@ -88,8 +101,27 @@ class EditableKey extends React.Component {
* @param {Event} evt - The event.
*/
handleKeyDown(evt) {
if (evt.keyCode === 27) {
this._node.blur();
var value = evt.target.value;
if (evt.keyCode === ESC) {
if (value.length === 0) {
this.element.remove();
} else {
this._node.blur();
}
}
}

/**
* If they key is a colon, tab to the next input.
*/
handleKeyUp(evt) {
if (evt.keyCode === COLON) {
var value = evt.target.value;
if (value !== ':') {
this.element.rename(value.replace(':', ''));
evt.target.value = '';
this._node.nextSibling.nextSibling.focus();
}
}
}

Expand All @@ -100,7 +132,7 @@ class EditableKey extends React.Component {
*/
handleChange(evt) {
var value = evt.target.value;
this._node.size = value.length;
this._node.size = inputSize(value);
if (this.isEditable()) {
if (this.element.isDuplicateKey(value)) {
this.setState({ duplicate: true });
Expand Down Expand Up @@ -130,12 +162,30 @@ class EditableKey extends React.Component {
}

/**
* Is this component editable?
* Is this component auto focusable?
*
* This is true if:
* - When a new element has been added and is a normal element.
* - When not being tabbed into.
*
* Is false if:
* - When a new array value has been added.
* - When the key is _id
*
* @returns {Boolean} If the component is editable.
*/
isAutoFocusable() {
return this.element.isAdded() && this.isEditable();
}

/**
* Is the key able to be edited?
*
* @returns {Boolean} If the key can be edited.
*/
isEditable() {
return this.element.isKeyEditable() && this.element.parentElement.currentType !== 'Array';
return this.element.isKeyEditable() &&
this.element.parent.currentType !== 'Array';
}

/**
Expand Down
Loading