mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-21 07:46:06 +08:00
feat(docs): Compile article docs with grunt docs
, better styling
This commit is contained in:
parent
ff15099f46
commit
f999de9b41
|
@ -49,6 +49,9 @@ thirdPartyClasses = {
|
|||
}
|
||||
|
||||
module.exports = (grunt) ->
|
||||
|
||||
{cp, mkdir, rm} = require('./task-helpers')(grunt)
|
||||
|
||||
getClassesToInclude = ->
|
||||
modulesPath = path.resolve(__dirname, '..', '..', 'internal_packages')
|
||||
classes = {}
|
||||
|
@ -127,6 +130,8 @@ module.exports = (grunt) ->
|
|||
if filename[0] is '_' and path.extname(filename) is '.html'
|
||||
Handlebars.registerPartial(filename[0..-6], grunt.file.read(abspath))
|
||||
|
||||
# Class-level documentation
|
||||
|
||||
templatePath = path.join(templatesPath, 'class.html')
|
||||
template = Handlebars.compile(grunt.file.read(templatePath))
|
||||
|
||||
|
@ -174,3 +179,26 @@ module.exports = (grunt) ->
|
|||
result = template(contents)
|
||||
resultPath = path.join(docsOutputDir, "#{classname}.html")
|
||||
grunt.file.write(resultPath, result)
|
||||
|
||||
# Conceptual documentation
|
||||
|
||||
articleTemplatePath = path.join(templatesPath, 'article.html')
|
||||
articleTemplate = Handlebars.compile(grunt.file.read(articleTemplatePath))
|
||||
|
||||
articlesPath = path.resolve(__dirname, '..', '..', 'docs')
|
||||
fs.traverseTreeSync articlesPath, (file) ->
|
||||
if path.extname(file) is '.md'
|
||||
content = grunt.file.read(file)
|
||||
for task in [marked, expandTypeReferences, expandFuncReferences]
|
||||
content = task(content)
|
||||
result = articleTemplate({content})
|
||||
resultPath = path.join(docsOutputDir, path.basename(file)[0..-3]+'html')
|
||||
grunt.file.write(resultPath, result)
|
||||
true
|
||||
|
||||
# Copy styles and images
|
||||
|
||||
imagesPath = path.resolve(__dirname, '..', '..', 'docs', 'images')
|
||||
cssPath = path.resolve(__dirname, '..', '..', 'docs', 'css')
|
||||
cp imagesPath, path.join(docsOutputDir, "images")
|
||||
cp cssPath, path.join(docsOutputDir, "css")
|
||||
|
|
2
docs-templates/_footer.html
Normal file
2
docs-templates/_footer.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
</body>
|
||||
</html>
|
|
@ -1,19 +1,28 @@
|
|||
<h4 id={{name}}>{{name}} <a href="#{{name}}">Link</a></h4>
|
||||
<h4 id={{name}} class="function-name">
|
||||
{{name}} <a href="#{{name}}" class="link"></a>
|
||||
</h4>
|
||||
<p>{{{description}}}</p>
|
||||
{{#if arguments.length}}
|
||||
Parameters
|
||||
<table>
|
||||
<strong>Parameters</strong>
|
||||
<table class="arguments">
|
||||
{{#each arguments}}
|
||||
<tr><td>{{name}}</td><td>{{type}}</td><td>{{{description}}}</td><td>{{isOptional}}</td></tr>
|
||||
<tr>
|
||||
<td style="width:15%;">
|
||||
<em>{{name}}</em>
|
||||
</td>
|
||||
<td>
|
||||
{{{description}}} {{#if isOptional}} (Optional) {{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/if}}
|
||||
|
||||
{{#if returnValues.length}}
|
||||
Returns
|
||||
<table>
|
||||
<strong>Returns</strong>
|
||||
<table class="arguments">
|
||||
{{#each returnValues}}
|
||||
<tr><td>{{type}}</td><td>{{{description}}}</td></tr>
|
||||
<tr><td style="width:15%;">{{type}}</td><td>{{{description}}}</td></tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/if}}
|
||||
|
|
19
docs-templates/_header.html
Normal file
19
docs-templates/_header.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<link rel="stylesheet" type="text/css" href="css/main.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="css/tomorrow.css">
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
<div style="border-bottom:1px solid #ccc;">
|
||||
<img src="images/edgehill.png" style="width:50px; height:50px; float:right;"/>
|
||||
<h2>Nylas Mail Developer Preview</h2>
|
||||
<span>Last Revised: May 8, 2015</span>
|
||||
</div>
|
||||
<div style="border-bottom:1px solid #ccc; padding:10px; margin-bottom:20px;">
|
||||
<a href="index.html">Home</a>
|
||||
<a href="api.html">API Reference</a>
|
||||
</div>
|
||||
-->
|
|
@ -1,9 +1,16 @@
|
|||
<h4 id={{name}}>{{name}} <a href="#{{name}}">Link</a></h4>
|
||||
<h4 id={{name}}>{{name}} <a href="#{{name}}" class="link"></a></h4>
|
||||
<p>{{{description}}}</p>
|
||||
{{#if arguments.length}}
|
||||
<table>
|
||||
<table class="arguments">
|
||||
{{#each arguments}}
|
||||
<tr><td>{{name}}</td><td>{{type}}</td><td>{{{description}}}</p></td><td>{{isOptional}}</td></tr>
|
||||
<tr>
|
||||
<td style="width:15%;">
|
||||
<em>{{name}}</em>
|
||||
</td>
|
||||
<td>
|
||||
{{{description}}} {{#if isOptional}} (Optional) {{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/if}}
|
||||
|
|
7
docs-templates/article.html
Normal file
7
docs-templates/article.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
{{>_header}}
|
||||
|
||||
<div class="article">
|
||||
{{{content}}}
|
||||
</div>
|
||||
|
||||
{{>_footer}}
|
|
@ -1,167 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<style>
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
color: #737373;
|
||||
background-color: white;
|
||||
margin: 10px 13px 10px 13px;
|
||||
}
|
||||
table {
|
||||
margin: 10px 0 15px 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td,th {
|
||||
vertical-align: top;
|
||||
border: 1px solid #ddd;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
th {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
{{>_header}}
|
||||
|
||||
a {
|
||||
color: #0069d6;
|
||||
}
|
||||
a:hover {
|
||||
color: #0050a3;
|
||||
text-decoration: none;
|
||||
}
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
p,td {
|
||||
margin-bottom: 9px;
|
||||
font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #404040;
|
||||
line-height: 36px;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 18px;
|
||||
font-size: 30px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
h6 {
|
||||
font-size: 13px;
|
||||
}
|
||||
hr {
|
||||
margin: 0 0 19px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
blockquote {
|
||||
padding: 13px 13px 21px 15px;
|
||||
margin-bottom: 18px;
|
||||
font-family:georgia,serif;
|
||||
font-style: italic;
|
||||
}
|
||||
blockquote:before {
|
||||
content:"\201C";
|
||||
font-size:40px;
|
||||
margin-left:-10px;
|
||||
font-family:georgia,serif;
|
||||
color:#eee;
|
||||
}
|
||||
blockquote p {
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
margin-bottom: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
code, pre {
|
||||
font-family: Monaco, Andale Mono, Courier New, monospace;
|
||||
}
|
||||
code {
|
||||
background-color: #fee9cc;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
padding: 1px 3px;
|
||||
font-size: 12px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
pre {
|
||||
display: block;
|
||||
padding: 14px;
|
||||
margin: 0 0 18px;
|
||||
line-height: 16px;
|
||||
font-size: 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
pre code {
|
||||
background-color: #fff;
|
||||
color:#737373;
|
||||
font-size: 11px;
|
||||
padding: 0;
|
||||
}
|
||||
sup {
|
||||
font-size: 0.83em;
|
||||
vertical-align: super;
|
||||
line-height: 0;
|
||||
}
|
||||
* {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
@media screen and (min-width: 914px) {
|
||||
body {
|
||||
width: 854px;
|
||||
margin:10px auto;
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
body,code,pre code,h1,h2,h3,h4,h5,h6 {
|
||||
color: black;
|
||||
}
|
||||
table, pre {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<title>asdasda</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>{{name}}{{#if superClass}} extends {{superClass}}{{/if}}</h2>
|
||||
<span>{{filename}}</span>
|
||||
<h2>{{name}}</h2>
|
||||
{{#if superClass}}
|
||||
<span>extends {{superClass}}</span>
|
||||
{{/if}}
|
||||
|
||||
<p>{{{description}}}</p>
|
||||
|
||||
|
@ -200,5 +42,4 @@ sup {
|
|||
|
||||
{{/if}}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{{>_footer}}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
### Flux Architecture
|
||||
### Application Architecture: Flux
|
||||
|
||||
Nylas Mail also uses [Reflux](https://github.com/spoike/refluxjs), a slim implementation of Facebook's [Flux Application Architecture](https://facebook.github.io/flux/) to coordinate the movement of data through the application. Flux is extremely well suited for applications that support third-party extension, because it emphasizes loose coupling and well defined interfaces between components. It enforces:
|
||||
|
||||
|
@ -35,4 +35,3 @@ For a complete list of available actions, see `Actions.coffee`. Actions in Nylas
|
|||
- Main Window Actions: These actions can be fired in any window and are automatically sent to the main window via IPC. They are not sent to other windows of the app.
|
||||
|
||||
- Window Actions: These actions only broadcast within the window they're fired in.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Nylas Mail is built on top of a custom database layer modeled after ActiveRecord. For many parts of the application, the database is the source of truth. Data is retrieved from the API, written to the database, and changes to the database trigger Stores and components to refresh their contents. The illustration below shows this flow of data:
|
||||
|
||||
<img src="./images/database-flow.png" style="max-width:750px;">
|
||||
<img src="./images/database-flow.png">
|
||||
|
||||
The Database connection is managed by the `DatabaseStore`, a singleton object that exists in every window. All Database requests are asynchronous. Queries are forwarded to the application's `Browser` process via IPC and run in SQLite.
|
||||
|
||||
|
@ -10,7 +10,7 @@ The Database connection is managed by the `DatabaseStore`, a singleton object th
|
|||
|
||||
In Nylas Mail, Models are thin wrappers around data with a particular schema. Each Model class declares a set of attributes that define the object's data. For example:
|
||||
|
||||
```
|
||||
```coffee
|
||||
class Example extends Model
|
||||
|
||||
@attributes:
|
||||
|
@ -43,7 +43,7 @@ When models are inflated from JSON using `fromJSON` or converted to JSON using `
|
|||
|
||||
The DatabaseStore automatically maintains cache tables for storing Model objects. By default, models are stored in the cache as JSON blobs and basic attributes are not queryable. When the `queryable` option is specified on an attribute, it is given a separate column and index in the SQLite table for the model, and you can construct queries using the attribute:
|
||||
|
||||
```
|
||||
```coffee
|
||||
Thread.attributes.namespaceId.equals("123")
|
||||
// where namespace_id = '123'
|
||||
|
||||
|
@ -58,7 +58,7 @@ Thread.attributes.lastMessageTimestamp.descending()
|
|||
|
||||
You can make queries for models stored in SQLite using a Promise-based ActiveRecord-style syntax. There is no way to make raw SQL queries against the local data store.
|
||||
|
||||
```
|
||||
```coffee
|
||||
DatabaseStore.find(Thread, '123').then (thread) ->
|
||||
# thread is a thread object
|
||||
|
||||
|
@ -100,7 +100,7 @@ Joined Data attributes allow you to store certain attributes of an object in a s
|
|||
|
||||
When building a query on a model with a JoinedData attribute, you need to call `include` to explicitly load the joined data attribute. The query builder will automatically perform a `LEFT OUTER JOIN` with the secondary table to retrieve the attribute:
|
||||
|
||||
```
|
||||
```coffee
|
||||
DatabaseStore.find(Message, '123').then (message) ->
|
||||
// message.body is undefined
|
||||
|
||||
|
@ -120,13 +120,13 @@ When Collection attributes are marked as `queryable`, the DatabaseStore automati
|
|||
|
||||
Collection attributes have an additional clause builder, `contains`:
|
||||
|
||||
```
|
||||
```coffee
|
||||
DatabaseStore.findAll(Thread).where([Thread.attributes.tags.contains('inbox')])
|
||||
```
|
||||
|
||||
This is equivalent to writing the following SQL:
|
||||
|
||||
```
|
||||
```sql
|
||||
SELECT `Thread`.`data` FROM `Thread` INNER JOIN `Thread-Tag` AS `M1` ON `M1`.`id` = `Thread`.`id` WHERE `M1`.`value` = 'inbox' ORDER BY `Thread`.`last_message_timestamp` DESC
|
||||
```
|
||||
|
||||
|
@ -136,13 +136,13 @@ For many parts of the application, the Database is the source of truth. Funnelin
|
|||
|
||||
Within Reflux Stores, you can listen to the DatabaseStore using the `listenTo` helper method:
|
||||
|
||||
```
|
||||
```coffee
|
||||
@listenTo(DatabaseStore, @_onDataChanged)
|
||||
```
|
||||
|
||||
Within generic code, you can listen to the DatabaseStore using this syntax:
|
||||
|
||||
```
|
||||
```coffee
|
||||
@unlisten = DatabaseStore.listen(@_onDataChanged, @)
|
||||
```
|
||||
|
||||
|
@ -163,6 +163,3 @@ Nylas Mail exposes a minimal Database API that exposes high-level methods for sa
|
|||
- Package code should not be tightly coupled to SQLite
|
||||
- Queries should be composed in a way that makes invalid queries impossible
|
||||
- All changes to the local database must be observable
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ To create a Draft Store Extension, subclass {DraftStoreExtension} and override t
|
|||
|
||||
This extension displays a warning before sending a draft that contains the names of competitor's products and if the user proceeds to send the draft containing the words, it appends a disclaimer.
|
||||
|
||||
```
|
||||
```coffee
|
||||
{DraftStoreExtension} = require 'inbox-exports'
|
||||
|
||||
class ProductsExtension extends DraftStoreExtension
|
||||
|
@ -37,4 +37,3 @@ class ProductsExtension extends DraftStoreExtension
|
|||
or trademarks used in context."
|
||||
session.changes.add(body: bodyWithWarning)
|
||||
```
|
||||
|
||||
|
|
|
@ -1,49 +1,60 @@
|
|||
##Nylas Package API
|
||||
|
||||
<img src="images/edgehill.png" class="center-logo"/>
|
||||
<h2 style="text-align:center;">Nylas Package API</h2>
|
||||
<p style="text-align:center; width:75%; margin:auto; margin-top:18px;">
|
||||
The Nylas Package API allows you to create powerful extensions to Nylas Mail. The client is built on top of Atom Shell and runs on Mac OS X, Windows, and Linux. It exposes rich APIs for working with the mail, contacts, and calendar and a robust local cache layer. Your packages can leverage NodeJS and other web technologies to create innovative new experiences.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<table class="no-border">
|
||||
<tr><td style="width:50%;">
|
||||
|
||||
###Installing Nylas Mail
|
||||
|
||||
<h4>Installing Nylas Mail</h4>
|
||||
<p>
|
||||
Nylas Mail is available for Mac, Windows, and Linux. Download the latest build for your platform below:
|
||||
|
||||
- [Mac OS X](https://edgehill.nylas.com/download?platform=darwin)
|
||||
- [Linux](https://edgehill.nylas.com/download?platform=linux)
|
||||
- [Windows](https://edgehill.nylas.com/download?platform=win32)
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="https://edgehill.nylas.com/download?platform=darwin">Mac OS X</a></li>
|
||||
<li><a href="https://edgehill.nylas.com/download?platform=linux">Linux</a></li>
|
||||
<li><a href="https://edgehill.nylas.com/download?platform=win32">Windows</a></li>
|
||||
</ul>
|
||||
|
||||
</td><td style="width:50%;">
|
||||
|
||||
###Package Architecture
|
||||
|
||||
<h4>Package Architecture</h4>
|
||||
<p>
|
||||
Packages lie at the heart of Nylas Mail. Each part of the core experience is a separate package that uses the Nilas Package API to add functionality to the client. Learn more about packages and create your first package.
|
||||
|
||||
- [Package Overview](./PackageOverview.md)
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="./PackageOverview.html">Package Overview</a></li>
|
||||
</ul>
|
||||
|
||||
</td></tr>
|
||||
<tr><td style="width:50%; vertical-align:top;">
|
||||
|
||||
### Dive Deeper
|
||||
<h4>Dive Deeper</h4>
|
||||
|
||||
- [Application Architecture](./Architecture.md)
|
||||
- [React & Component Injection](./React.md)
|
||||
- [Core Interface Concepts](./InterfaceConcepts.md)
|
||||
- [Accessing the Database](./Database.md)
|
||||
- [Draft Store Extensions](./DraftStoreExtensions.md)
|
||||
<ul>
|
||||
<li><a href="./Architecture.html">Application Architecture</a></li>
|
||||
<li><a href="./React.html">React & Component Injection</a></li>
|
||||
<li><a href="./InterfaceConcepts.html">Core Interface Concepts</a></li>
|
||||
<li><a href="./Database.html">Accessing the Database</a></li>
|
||||
<li><a href="./DraftStoreExtensions.html">Draft Store Extensions</a></li>
|
||||
</ul>
|
||||
|
||||
</td><td style="width:50%; vertical-align:top;">
|
||||
|
||||
### Debugging Packages
|
||||
|
||||
<h4>Debugging Packages</h4>
|
||||
<p>
|
||||
Nylas Mail is built on top of Electron, which runs the latest version of Chromium. Learn how to access debug tools in Electron and use our Developer Tools Extensions:
|
||||
|
||||
- [Debugging in Nylas](./Debugging.md)
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="./Debugging.html">Debugging in Nylas</a></li>
|
||||
</ul>
|
||||
|
||||
</td></tr>
|
||||
<tr colspan="2"><td>
|
||||
##### Questions?
|
||||
|
||||
Need help? Check out the [FAQ](./FAQ.md) or post a question in the [Nylas Mail Facebook Group](facebook.com/groups/nylas.mail)
|
||||
<h4>Questions?</h4>
|
||||
<p>
|
||||
Need help? Check out the [FAQ](./FAQ.html) or post a question in the [Nylas Mail Facebook Group](facebook.com/groups/nylas.mail)
|
||||
</p>
|
||||
|
||||
</td></tr>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
The Nylas Mail user interface is conceptually organized into Sheets. Each Sheet represents a window of content. For example, the `Threads` sheet lies at the heart of the application. When the user chooses the "Files" tab, a separate `Files` sheet is displayed in place of `Threads`. When the user clicks a thread in single-pane mode, a `Thread` sheet is pushed on to the workspace and appears after a brief transition.
|
||||
|
||||
<img src="./images/sheets.png" style="max-width:400px;">
|
||||
<img src="./images/sheets.png">
|
||||
|
||||
The `WorkspaceStore` maintains the state of the application's workspace and the stack of sheets currently being displayed. Your packages can declare "root" sheets which are listed in the app's main sidebar, or push custom sheets on top of sheets to display data.
|
||||
|
||||
|
@ -12,9 +12,9 @@ The Nilas Workspace supports two display modes: `split` and `list`. Each Sheet d
|
|||
|
||||
For each mode, Sheets register a set of column names.
|
||||
|
||||
<img src="./images/columns.png" style="max-width:800px;">
|
||||
<img src="./images/columns.png">
|
||||
|
||||
```
|
||||
```coffee
|
||||
@defineSheet 'Threads', {root: true},
|
||||
split: ['RootSidebar', 'ThreadList', 'MessageList', 'MessageListSidebar']
|
||||
list: ['RootSidebar', 'ThreadList']
|
||||
|
@ -25,7 +25,7 @@ Column names are important. Once you've registered a sheet, your package (and ot
|
|||
Sheets also have a `Header` and `Footer` region that spans all of their content columns. You can register components to appear in these regions to display notifications, add bars beneath the toolbar, etc.
|
||||
|
||||
|
||||
```
|
||||
```coffee
|
||||
ComponentRegistry.register AccountSidebar,
|
||||
location: WorkspaceStore.Location.RootSidebar
|
||||
|
||||
|
@ -42,11 +42,11 @@ Each column is laid out as a CSS Flexbox, making them extremely flexible. For mo
|
|||
|
||||
Toolbars in Nylas Mail are also powered by the Component Registry. Though toolbars appear to be a single unit at the top of a sheet, they are divided into columns with the same widths as the columns in the sheet beneath them.
|
||||
|
||||
<img src="./images/toolbar.png" style="max-width:800px;">
|
||||
<img src="./images/toolbar.png">
|
||||
|
||||
Each Toolbar column is laid out using Flexbox. You can control where toolbar elements appear within the column using the CSS `order` attribute. To make it easy to position toolbar items on the left, right, or center of a column, we've added two "spacer" elements with `order:50` and `order:-50` that evenly use up available space. Other CSS attributes allow you to control whether your items shrink or expand as the column's size changes.
|
||||
|
||||
<img src="./images/toolbar-column.png" style="max-width:800px;">
|
||||
<img src="./images/toolbar-column.png">
|
||||
|
||||
To add items to a toolbar, you inject them via the Component Registry. There are several ways of describing the location of a toolbar component which are useful in different scenarios:
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ Our package also contains source files, a spec file with complete tests for the
|
|||
|
||||
`package.json` lists `lib/main` as the root file of our package. As our package expands, we can add other source files. Since Nylas Mail runs NodeJS, you can `require` other source files, Node packages, etc. Inside `main.cjsx`, there are two important functions being exported:
|
||||
|
||||
```
|
||||
```coffee
|
||||
module.exports =
|
||||
|
||||
##
|
||||
|
|
|
@ -15,27 +15,27 @@ To use a standard component, require it from `ui-components` and use it in your
|
|||
|
||||
Here's a quick look at standard components you can require from `ui-components`:
|
||||
|
||||
- **Menu**: Allows you to display a list of items consistent with the rest of the Nylas Mail user experience.
|
||||
- **{Menu}**: Allows you to display a list of items consistent with the rest of the Nylas Mail user experience.
|
||||
|
||||
- **Spinner**: Displays an indeterminate progress indicator centered within it's container.
|
||||
- **{Spinner}**: Displays an indeterminate progress indicator centered within it's container.
|
||||
|
||||
- **Popover**: Component for creating menus and popovers that appear in response to a click and stay open until the user clicks outside them.
|
||||
- **{Popover}**: Component for creating menus and popovers that appear in response to a click and stay open until the user clicks outside them.
|
||||
|
||||
- **Flexbox**: Component for creating a Flexbox layout.
|
||||
- **{Flexbox}**: Component for creating a Flexbox layout.
|
||||
|
||||
- **RetinaImg**: Replacement for standard `<img>` tags which automatically resolves the best version of the image for the user's display and can apply many image transforms.
|
||||
- **{RetinaImg}**: Replacement for standard `<img>` tags which automatically resolves the best version of the image for the user's display and can apply many image transforms.
|
||||
|
||||
- **ListTabular**: Component for creating a list of items backed by a paginating ModelView.
|
||||
- **{ListTabular}**: Component for creating a list of items backed by a paginating ModelView.
|
||||
|
||||
- **MultiselectList**: Component for creating a list that supports multi-selection. (Internally wraps ListTabular)
|
||||
- **{MultiselectList}**: Component for creating a list that supports multi-selection. (Internally wraps ListTabular)
|
||||
|
||||
- **MultiselectActionBar**: Component for creating a contextual toolbar that is activated when the user makes a selection on a ModelView.
|
||||
- **{MultiselectActionBar}**: Component for creating a contextual toolbar that is activated when the user makes a selection on a ModelView.
|
||||
|
||||
- **ResizableRegion**: Component that renders it's children inside a resizable region with a draggable handle.
|
||||
- **{ResizableRegion}**: Component that renders it's children inside a resizable region with a draggable handle.
|
||||
|
||||
- **TokenizingTextField**: Wraps a standard `<input>` and takes function props for tokenizing input values and displaying autocompletion suggestions.
|
||||
- **{TokenizingTextField}**: Wraps a standard `<input>` and takes function props for tokenizing input values and displaying autocompletion suggestions.
|
||||
|
||||
- **EventedIFrame**: Replacement for the standard `<iframe>` tag which handles events directed at the iFrame to ensure a consistent user experience.
|
||||
- **{EventedIFrame}**: Replacement for the standard `<iframe>` tag which handles events directed at the iFrame to ensure a consistent user experience.
|
||||
|
||||
### React Component Injection
|
||||
|
||||
|
@ -47,7 +47,7 @@ Once you've created components, the next step is to register them with the {Comp
|
|||
|
||||
It's easy to see where registered components are displayed in Nylas Mail. Enable the Developer bar at the bottom of the app by opening the Inspector panel, and then click "**Component Regions**":
|
||||
|
||||
<img src="./images/injected-components.png" style="max-width:800px;">
|
||||
<img src="./images/injected-components.png">
|
||||
|
||||
Each region outlined in red is filled dynamically by looking up a React component or set of components from the Component Registry. You can see the role or location you'd need to register for, and the `props` that your component will receive in those locations.
|
||||
|
||||
|
@ -55,21 +55,21 @@ Here are a few examples of how to use it to extend Nylas Mail:
|
|||
|
||||
1. Add a component to the Thread List column:
|
||||
|
||||
```
|
||||
```coffee
|
||||
ComponentRegistry.register ThreadList,
|
||||
location: WorkspaceStore.Location.ThreadList
|
||||
```
|
||||
|
||||
2. Add a component to the action bar at the bottom of the Composer:
|
||||
|
||||
```
|
||||
```coffee
|
||||
ComponentRegistry.register TemplatePicker,
|
||||
role: 'Composer:ActionButton'
|
||||
```
|
||||
|
||||
3. Replace the `Participants` component that ships with Nylas Mail to display thread participants on your own:
|
||||
|
||||
```
|
||||
```coffee
|
||||
ComponentRegistry.register ParticipantsWithStatusDots,
|
||||
role: 'Participants'
|
||||
```
|
||||
|
@ -85,7 +85,7 @@ There are also several convenience components that make it easy to dynamically i
|
|||
|
||||
- {InjectedComponent}: Renders the first component for the `matching` criteria you provide, and passes it the props in `externalProps`. See the API reference for more information.
|
||||
|
||||
```
|
||||
```coffee
|
||||
<InjectedComponent
|
||||
matching={role:"Attachment"}
|
||||
exposedProps={file: file, messageLocalId: @props.localId}/>
|
||||
|
@ -93,7 +93,7 @@ There are also several convenience components that make it easy to dynamically i
|
|||
|
||||
- {InjectedComponentSet}: Renders all of the components `matching` criteria you provide inside a {Flexbox}, and passes it the props in `externalProps`. See the API reference for more information.
|
||||
|
||||
```
|
||||
```coffee
|
||||
<InjectedComponentSet
|
||||
className="message-actions"
|
||||
matching={role:"MessageAction"}
|
||||
|
@ -104,6 +104,6 @@ There are also several convenience components that make it easy to dynamically i
|
|||
|
||||
Nylas Mail considers all injected components "unsafe". When you render them using {InjectedComponent} or {InjectedComponentSet}, they will be wrapped in a component that prevents exceptions in their React render and lifecycle methods from impacting your component. Instead of your component triggering a React Invariant exception in the application, an exception notice will be rendered in place of the unsafe component.
|
||||
|
||||
<img src="./images/unsafe-component-exception.png" style="max-width:800px;">
|
||||
<img src="./images/unsafe-component-exception.png">
|
||||
|
||||
In the future, Nylas Mail may automatically disable packages when their React components throw exceptions.
|
||||
|
|
223
docs/css/main.css
Normal file
223
docs/css/main.css
Normal file
|
@ -0,0 +1,223 @@
|
|||
|
||||
@font-face {
|
||||
font-family: 'FaktPro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('FaktPro-Blond'), url('fonts/FaktPro-Blond.ttf'), local('Helvetica Neue'), local('Helvetica');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'FaktPro';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('FaktPro-Medium'), url('fonts/FaktPro-Medium.ttf'), local('Helvetica Neue'), local('Helvetica');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'FaktPro';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('FaktPro-SemiBold'), url('fonts/FaktPro-SemiBold.ttf'), local('Helvetica Neue'), local('Helvetica');
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
strong {
|
||||
color: #404040;
|
||||
}
|
||||
body {
|
||||
font-family: 'FaktPro';
|
||||
font-weight: normal;
|
||||
font-size: 17px;
|
||||
line-height: 1.5em;
|
||||
color: #737373;
|
||||
background-color: white;
|
||||
margin: 10px 13px 10px 13px;
|
||||
}
|
||||
table {
|
||||
margin: 10px 0 15px 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td,th {
|
||||
vertical-align: top;
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
th {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0069d6;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #0050a3;
|
||||
text-decoration: none;
|
||||
}
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #404040;
|
||||
line-height: 36px;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 18px;
|
||||
font-size: 30px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 30px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
margin-top:30px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
margin-top:20px;
|
||||
}
|
||||
h5 {
|
||||
font-size: 16px;
|
||||
}
|
||||
h6 {
|
||||
font-size: 14px;
|
||||
}
|
||||
hr {
|
||||
margin: 0 0 19px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
blockquote {
|
||||
padding: 13px 13px 21px 15px;
|
||||
margin-bottom: 18px;
|
||||
font-family:georgia,serif;
|
||||
font-style: italic;
|
||||
}
|
||||
blockquote:before {
|
||||
content:"\201C";
|
||||
font-size:40px;
|
||||
margin-left:-10px;
|
||||
font-family:georgia,serif;
|
||||
color:#eee;
|
||||
}
|
||||
blockquote p {
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
margin-bottom: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
img {
|
||||
max-width:100%;
|
||||
}
|
||||
code, pre {
|
||||
font-family: Monaco, Andale Mono, Courier New, monospace;
|
||||
}
|
||||
code {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
background-color: #fee9cc;
|
||||
padding: 1px 3px;
|
||||
font-size: 14px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
pre {
|
||||
margin: 0 0 18px;
|
||||
line-height: 18px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #d9d9d9;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
pre code {
|
||||
font-size: 13px;
|
||||
background-color: white;
|
||||
padding: 0;
|
||||
display: block;
|
||||
padding: 14px;
|
||||
}
|
||||
sup {
|
||||
font-size: 0.83em;
|
||||
vertical-align: super;
|
||||
line-height: 0;
|
||||
}
|
||||
* {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
.link {
|
||||
width:18px;
|
||||
height:18px;
|
||||
display:inline-block;
|
||||
vertical-align:sub;
|
||||
opacity:0.4;
|
||||
background:url(../images/link.png) top left;
|
||||
background-size:cover;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 914px) {
|
||||
body {
|
||||
width: 854px;
|
||||
margin:10px auto;
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
body,code,pre code,h1,h2,h3,h4,h5,h6 {
|
||||
color: black;
|
||||
}
|
||||
table, pre {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.article img {
|
||||
margin-top:18px;
|
||||
margin-bottom:18px;
|
||||
border:1px solid #ccc;
|
||||
}
|
||||
|
||||
.article .center-logo {
|
||||
width:270px;
|
||||
height:270px;
|
||||
margin:auto;
|
||||
margin-top:20px;
|
||||
display:block;
|
||||
border:0;
|
||||
}
|
||||
|
||||
.function-name {
|
||||
border-bottom:1px solid #ccc;
|
||||
padding-top:10px;
|
||||
}
|
||||
|
||||
table.arguments {
|
||||
width:100%;
|
||||
}
|
||||
table.arguments p {
|
||||
margin-bottom:0;
|
||||
}
|
||||
|
||||
table.no-border td {
|
||||
border:0;
|
||||
}
|
93
docs/css/tomorrow.css
Normal file
93
docs/css/tomorrow.css
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
|
||||
/* Tomorrow Comment */
|
||||
.hljs-comment {
|
||||
color: #8e908c;
|
||||
}
|
||||
|
||||
/* Tomorrow Red */
|
||||
.hljs-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-tag,
|
||||
.hljs-regexp,
|
||||
.ruby .hljs-constant,
|
||||
.xml .hljs-tag .hljs-title,
|
||||
.xml .hljs-pi,
|
||||
.xml .hljs-doctype,
|
||||
.html .hljs-doctype,
|
||||
.css .hljs-id,
|
||||
.css .hljs-class,
|
||||
.css .hljs-pseudo {
|
||||
color: #c82829;
|
||||
}
|
||||
|
||||
/* Tomorrow Orange */
|
||||
.hljs-number,
|
||||
.hljs-preprocessor,
|
||||
.hljs-pragma,
|
||||
.hljs-built_in,
|
||||
.hljs-literal,
|
||||
.hljs-params,
|
||||
.hljs-constant {
|
||||
color: #f5871f;
|
||||
}
|
||||
|
||||
/* Tomorrow Yellow */
|
||||
.ruby .hljs-class .hljs-title,
|
||||
.css .hljs-rule .hljs-attribute {
|
||||
color: #eab700;
|
||||
}
|
||||
|
||||
/* Tomorrow Green */
|
||||
.hljs-string,
|
||||
.hljs-value,
|
||||
.hljs-inheritance,
|
||||
.hljs-header,
|
||||
.hljs-name,
|
||||
.ruby .hljs-symbol,
|
||||
.xml .hljs-cdata {
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
/* Tomorrow Aqua */
|
||||
.hljs-title,
|
||||
.css .hljs-hexcolor {
|
||||
color: #3e999f;
|
||||
}
|
||||
|
||||
/* Tomorrow Blue */
|
||||
.hljs-function,
|
||||
.python .hljs-decorator,
|
||||
.python .hljs-title,
|
||||
.ruby .hljs-function .hljs-title,
|
||||
.ruby .hljs-title .hljs-keyword,
|
||||
.perl .hljs-sub,
|
||||
.javascript .hljs-title,
|
||||
.coffeescript .hljs-title {
|
||||
color: #4271ae;
|
||||
}
|
||||
|
||||
/* Tomorrow Purple */
|
||||
.hljs-keyword,
|
||||
.javascript .hljs-function {
|
||||
color: #8959a8;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: white;
|
||||
color: #4d4d4c;
|
||||
padding: 0.5em;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
.coffeescript .javascript,
|
||||
.javascript .xml,
|
||||
.tex .hljs-formula,
|
||||
.xml .javascript,
|
||||
.xml .vbscript,
|
||||
.xml .css,
|
||||
.xml .hljs-cdata {
|
||||
opacity: 0.5;
|
||||
}
|
BIN
docs/images/edgehill.png
Normal file
BIN
docs/images/edgehill.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
BIN
docs/images/link.png
Normal file
BIN
docs/images/link.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 608 B |
|
@ -13,7 +13,7 @@ Public: InjectedComponent makes it easy to include a set of dynamically register
|
|||
components inside of your React render method. Rather than explicitly render
|
||||
an array of buttons, for example, you can use InjectedComponentSet:
|
||||
|
||||
```
|
||||
```coffee
|
||||
<InjectedComponentSet className="message-actions"
|
||||
matching={role: 'ThreadActionButton'}
|
||||
exposedProps={thread:@props.thread, message:@props.message}>
|
||||
|
|
|
@ -12,7 +12,7 @@ Public: InjectedComponent makes it easy to include dynamically registered
|
|||
components inside of your React render method. Rather than explicitly render
|
||||
a component, such as a `<Composer>`, you can use InjectedComponent:
|
||||
|
||||
```
|
||||
```coffee
|
||||
<InjectedComponent matching={role:"Composer"} exposedProps={draftId:123} />
|
||||
```
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ When the provided `dataStore` has a selection, it appears over the other items i
|
|||
Generally, you wrap {MultiselectActionBar} in your own simple component to provide a dataStore
|
||||
and other settings:
|
||||
|
||||
```
|
||||
```coffee
|
||||
class MultiselectActionBar extends React.Component
|
||||
@displayName: 'MultiselectActionBar'
|
||||
|
||||
|
@ -29,7 +29,7 @@ class MultiselectActionBar extends React.Component
|
|||
The MultiselectActionBar uses the `ComponentRegistry` to find items to display for the given
|
||||
collection name. To add an item to the bar created in the example above, register it like this:
|
||||
|
||||
```
|
||||
```coffee
|
||||
ComponentRegistry.register ThreadBulkArchiveButton,
|
||||
role: 'thread:BulkAction'
|
||||
```
|
||||
|
|
|
@ -2,9 +2,6 @@ React = require 'react/addons'
|
|||
_ = require 'underscore-plus'
|
||||
{CompositeDisposable} = require 'event-kit'
|
||||
|
||||
###
|
||||
###
|
||||
|
||||
class QuotedTextToggleButton extends React.Component
|
||||
@displayName: "QuotedTextToggleButton"
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ This is adapted from Facebook's CSSTransitionGroup which is in the React
|
|||
addons and under the Apache 2.0 License.
|
||||
|
||||
Example:
|
||||
```
|
||||
```coffee
|
||||
<TimeoutTransitionGroup
|
||||
leaveTimeout={125}
|
||||
enterTimeout={125}
|
||||
|
|
|
@ -13,7 +13,7 @@ your `flex` and `order` values to be one level too deep. For these scenarios,
|
|||
UnsafeComponent looks for `containerStyles` on your React component and attaches
|
||||
them to the wrapper div:
|
||||
|
||||
```
|
||||
```coffee
|
||||
class MyComponent extends React.Component
|
||||
@displayName: 'MyComponent'
|
||||
@containerStyles:
|
||||
|
|
|
@ -15,13 +15,13 @@ collection.
|
|||
|
||||
Collection attributes have an additional clause builder, `contains`:
|
||||
|
||||
```
|
||||
```coffee
|
||||
DatabaseStore.findAll(Thread).where([Thread.attributes.tags.contains('inbox')])
|
||||
```
|
||||
|
||||
This is equivalent to writing the following SQL:
|
||||
|
||||
```
|
||||
```sql
|
||||
SELECT `Thread`.`data` FROM `Thread`
|
||||
INNER JOIN `Thread-Tag` AS `M1` ON `M1`.`id` = `Thread`.`id`
|
||||
WHERE `M1`.`value` = 'inbox'
|
||||
|
|
|
@ -17,7 +17,7 @@ to call `include` to explicitly load the joined data attribute.
|
|||
The query builder will automatically perform a `LEFT OUTER JOIN` with
|
||||
the secondary table to retrieve the attribute:
|
||||
|
||||
```
|
||||
```coffee
|
||||
DatabaseStore.find(Message, '123').then (message) ->
|
||||
# message.body is undefined
|
||||
|
||||
|
|
|
@ -2,10 +2,35 @@ _ = require 'underscore-plus'
|
|||
{tableNameForJoin} = require '../models/utils'
|
||||
|
||||
###
|
||||
Public: The Matcher class encapsulates a particular comparison clause on an attribute.
|
||||
Matchers can evaluate whether or not an object matches them, and in the future
|
||||
they will also compose WHERE clauses. Each matcher has a reference to a model
|
||||
Public: The Matcher class encapsulates a particular comparison clause on an {Attribute}.
|
||||
Matchers can evaluate whether or not an object matches them, and also compose
|
||||
SQL clauses for the DatabaseStore. Each matcher has a reference to a model
|
||||
attribute, a comparator and a value.
|
||||
|
||||
```coffee
|
||||
|
||||
# Retrieving Matchers
|
||||
|
||||
isUnread = Thread.attributes.unread.equal(true)
|
||||
|
||||
isInInbox = Thread.attributes.tags.contains('inbox')
|
||||
|
||||
# Using Matchers in Database Queries
|
||||
|
||||
DatabaseStore.findAll(Thread).where(isUnread)...
|
||||
|
||||
# Using Matchers to test Models
|
||||
|
||||
threadA = new Thread(unread: true)
|
||||
threadB = new Thread(unread: false)
|
||||
|
||||
isUnread.evaluate(threadA)
|
||||
# => true
|
||||
isUnread.evaluate(threadB)
|
||||
# => false
|
||||
|
||||
```
|
||||
|
||||
###
|
||||
class Matcher
|
||||
constructor: (@attr, @comparator, @val) ->
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
###
|
||||
Public: Represents a particular sort direction on a particular column. You should not
|
||||
instantiate SortOrders manually. Instead, call `Attribute.ascending()` or
|
||||
`Attribute.descending()` to obtain a sort order.
|
||||
instantiate SortOrders manually. Instead, call {Attribute::ascending} or
|
||||
{Attribute::descending} to obtain a sort order instance:
|
||||
|
||||
```coffee
|
||||
DatabaseStore.findBy(Message)
|
||||
.where({threadId: threadId, draft: false})
|
||||
.order(Message.attributes.date.descending()).then (messages) ->
|
||||
|
||||
```
|
||||
###
|
||||
class SortOrder
|
||||
constructor: (@attr, @direction = 'DESC') ->
|
||||
|
|
|
@ -13,7 +13,7 @@ and {DatabaseStore::unpersistModel}.
|
|||
|
||||
**Simple Example:** Fetch a thread
|
||||
|
||||
```
|
||||
```coffee
|
||||
query = DatabaseStore.find(Thread, '123a2sc1ef4131')
|
||||
query.then (thread) ->
|
||||
# thread or null
|
||||
|
@ -21,7 +21,7 @@ query.then (thread) ->
|
|||
|
||||
**Advanced Example:** Fetch 50 threads in the inbox, in descending order
|
||||
|
||||
```
|
||||
```coffee
|
||||
query = DatabaseStore.findAll(Thread)
|
||||
query.where([Thread.attributes.tagIds.contains('inbox')])
|
||||
.order([Thread.attributes.lastMessageTimestamp.descending()])
|
||||
|
@ -125,7 +125,7 @@ class ModelQuery
|
|||
|
||||
# Public:
|
||||
#
|
||||
# - `offset` {Number] The start offset of the query.
|
||||
# - `offset` {Number} The start offset of the query.
|
||||
#
|
||||
# This method is chainable.
|
||||
#
|
||||
|
@ -160,7 +160,7 @@ class ModelQuery
|
|||
|
||||
# Public: Starts query execution and returns a Promise.
|
||||
#
|
||||
# Returns A {Promise} that resolves with the Models returned by the
|
||||
# Returns a {Promise} that resolves with the Models returned by the
|
||||
# query, or rejects with an error from the Database layer.
|
||||
#
|
||||
then: (next) ->
|
||||
|
|
|
@ -19,7 +19,7 @@ The ContactStore monitors the {DatabaseStore} for changes to {Contact} models
|
|||
and triggers when contacts have changed, allowing your stores and components
|
||||
to refresh data based on the ContactStore.
|
||||
|
||||
```
|
||||
```coffee
|
||||
@unsubscribe = ContactStore.listen(@_onContactsChanged, @)
|
||||
|
||||
_onContactsChanged: ->
|
||||
|
|
|
@ -111,7 +111,7 @@ rest of the application in sync.
|
|||
To listen for changes to the local cache, subscribe to the DatabaseStore and
|
||||
inspect the changes that are sent to your listener method.
|
||||
|
||||
```
|
||||
```coffeescript
|
||||
@unsubscribe = DatabaseStore.listen(@_onDataChanged, @)
|
||||
|
||||
...
|
||||
|
@ -382,7 +382,7 @@ class DatabaseStore
|
|||
# - `id` The {String} id of the {Model} you're trying to retrieve
|
||||
#
|
||||
# Example:
|
||||
# ```
|
||||
# ```coffee
|
||||
# DatabaseStore.find(Thread, 'id-123').then (thread) ->
|
||||
# # thread is a Thread object, or null if no match was found.
|
||||
# ```
|
||||
|
|
|
@ -7,7 +7,7 @@ To register your extension with the DraftStore, call {DraftStore::registerExtens
|
|||
When your package is being unloaded, you *must* call the corresponding
|
||||
{DraftStore::unregisterExtension} to unhook your extension.
|
||||
|
||||
```
|
||||
```coffee
|
||||
activate: ->
|
||||
DraftStore.registerExtension(MyExtension)
|
||||
|
||||
|
@ -51,7 +51,7 @@ class DraftStoreExtension
|
|||
|
||||
Example:
|
||||
|
||||
```
|
||||
```coffee
|
||||
# Remove any <code> tags found in the draft body
|
||||
finalizeSessionBeforeSending: (session) ->
|
||||
body = session.draft().body
|
||||
|
@ -122,7 +122,7 @@ class DraftStoreExtension
|
|||
`<code>` tag placed in the body and change it's CSS class to reflect that it is no
|
||||
longer empty.
|
||||
|
||||
```
|
||||
```coffee
|
||||
onInput: (editableNode, event) ->
|
||||
selection = document.getSelection()
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ class DraftStore
|
|||
#
|
||||
# Example:
|
||||
#
|
||||
# ```
|
||||
# ```coffee
|
||||
# session = DraftStore.sessionForLocalId(localId)
|
||||
# session.prepare().then ->
|
||||
# # session.draft() is now ready
|
||||
|
|
Loading…
Reference in a new issue