Merge pull request #501 from owncloud/app-nav

ownCloud 8: Add app developer documentation for list entry actions
This commit is contained in:
Carla Schroder 2014-12-04 10:39:13 -08:00
commit 6be12f796f
2 changed files with 192 additions and 2 deletions

View File

@ -322,6 +322,7 @@ By default there is only a responder for JSON but more can be added easily:
namespace OCA\MyApp\Controller;
use \OCP\AppFramework\Controller;
use \OCP\AppFramework\Http\DataResponse;
class PageController extends Controller {
@ -329,7 +330,15 @@ By default there is only a responder for JSON but more can be added easily:
// XMLResponse has to be implemented
$this->registerResponder('xml', function($value) {
return new XMLResponse($value);
if ($value instanceof DataResponse) {
return new XMLResponse(
$value->getData(),
$value->getStatus(),
$value->getHeaders()
);
} else {
return new XMLResponse($value);
}
});
return array('test' => 'hi');
@ -339,6 +348,32 @@ By default there is only a responder for JSON but more can be added easily:
.. note:: The above example would only return XML if the **format** parameter was *xml*. If you want to return an XMLResponse regardless of the format parameter, extend the Response class and return a new instance of it from the controller method instead.
.. versionadded:: 8
Because returning values works fine in case of a success but not in case of failure that requires a custom HTTP error code, you can always wrap the value in a **DataResponse**. This works for both normal responses and error responses.
.. code-block:: php
<?php
namespace OCA\MyApp\Controller;
use \OCP\AppFramework\Controller;
use \OCP\AppFramework\Http\DataResponse;
use \OCP\AppFramework\Http\Http;
class PageController extends Controller {
public function returnHi() {
try {
return new DataResponse(calculate_hi());
} catch (\Exception $ex) {
return new DataResponse(array('msg' => 'not found!'), Http::STATUS_NOT_FOUND);
}
}
}
Templates
---------
A :doc:`template <templates>` can be rendered by returning a TemplateResponse. A TemplateResponse takes the following parameters:

View File

@ -37,9 +37,15 @@ To use the commonly used layout consisting of sidebar navigation and content the
<div id="app">
<div id="app-navigation">Your navigation</div>
<div id="app-content">Your content</div>
<div id="app-content">
<div id="app-content-wrapper">
Your content in here
</div>
</div>
</div>
For built in mobile support your content has to be wrapped inside another div with the id **app-content-wrapper**.
Navigation
==========
ownCloud provides a default CSS navigation layout. If list entries should have 16x16 px icons, the **with-icon** class can be added to the base **ul**. The maximum supported indention level is two, further indentions are not recommended.
@ -102,6 +108,155 @@ The class which should be applied to a first level element (**li**) that hosts o
</ul>
</div>
Menus
-----
.. versionadded:: 8
To add actions that affect the current list element you can add a menu for second and/or first level elements by adding the button and menu inside the corresponding **li** element and adding the **with-menu** css class:
.. code-block:: html
<div id="app-navigation">
<ul>
<li class="with-counter with-menu">
<a href="#">First level entry</a>
<div class="app-navigation-entry-utils">
<ul>
<li class="app-navigation-entry-utils-counter">15</li>
<li class="app-navigation-entry-utils-menu-button svg"><button></button></li>
</ul>
</div>
<div class="app-navigation-entry-menu open">
<ul>
<li><button class="icon-rename svg" title="rename"></button></li>
<li><button class="icon-delete svg" title="delete"></button></li>
</ul>
</div>
</li>
</div>
The div with the class **app-navigation-entry-utils** contains only the button (class: **app-navigation-entry-utils-menu-button**) to display the menu but in many cases another entry is needed to display some sort of count (mails count, unread feed count, etc.). In that case add the **with-counter** class to the list entry to adjust the correct padding and text-oveflow of the entry's title.
The count should be limitted to 999 and turn to 999+ if any higher number is given. If AngularJS is used the following filter can be used to get the correct behaviour:
.. code-block:: js
app.filter('counterFormatter', function () {
'use strict';
return function (count) {
if (count > 999) {
return '999+';
}
return count;
};
});
Use it like this:
.. code-block:: html
<li class="app-navigation-entry-utils-counter">{{ count | counterFormatter }}</li>
The menu is hidden by default (**display: none**) and has to be triggered by adding the **open** class to the **app-navigation-entry-menu** div.
In case of AngularJS the following small directive can be added to handle all the display and click logic out of the box:
.. code-block:: js
app.run(function ($document, $rootScope) {
'use strict';
$document.click(function (event) {
$rootScope.$broadcast('documentClicked', event);
});
});
app.directive('appNavigationEntryUtils', function () {
'use strict';
return {
restrict: 'C',
link: function (scope, elm) {
var menu = elm.siblings('.app-navigation-entry-menu');
var button = $(elm)
.find('.app-navigation-entry-utils-menu-button button');
button.click(function () {
menu.toggleClass('open');
});
scope.$on('documentClicked', function (scope, event) {
if (event.target !== button[0]) {
menu.removeClass('open');
}
});
}
};
});
Editing
-------
.. versionadded:: 8
Often an edit option is needed an entry. To add one for a given entry simply hide the title and add the following div inside the entry:
.. code-block:: html
<div id="app-navigation">
<ul class="with-icon">
<li>
<a href="#" class="hidden">First level entry</a>
<div class="app-navigation-entry-edit">
<form>
<input type="text" value="First level entry" autofocus-on-insert>
<input type="submit" value="" class="action icon-checkmark">
</form>
</div>
</li>
</ul>
</div>
If AngularJS is used you want to autofocus the input box. This can be achieved by placing the show condition inside an **ng-if** on the **app-navigation-entry-edit** div and adding the following directive:
.. code-block:: js
app.directive('autofocusOnInsert', function () {
'use strict';
return function (scope, elm) {
elm.focus();
};
});
**ng-if** is required because it removes/inserts the element into the DOM dynamically instead of just adding a **display: none** to it like **ng-show** and **ng-hide**.
Undo entry
----------
.. versionadded:: 8
If you want to undo a performed action on a navigation entry such as deletion, you should show the undo directly in place of the entry and make it disappear after location change or 7 seconds:
.. code-block:: html
<div id="app-navigation">
<ul class="with-icon">
<li>
<a href="#" class="hidden">First level entry</a>
<div class="app-navigation-entry-deleted">
<div class="app-navigation-entry-deleted-description">Deleted X</div>
<button class="app-navigation-entry-deleted-button icon-history" title="Undo"></button>
</div>
</li>
</ul>
</div>
Settings Area
=============