mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-12-29 20:04:59 +08:00
update(examples): Add github sidebar and translate examples
This commit is contained in:
parent
abf91a16e6
commit
a0c225219a
37 changed files with 1802 additions and 0 deletions
1
examples/N1-Composer-Translate/.gitignore
vendored
Executable file
1
examples/N1-Composer-Translate/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
node_modules
|
12
examples/N1-Composer-Translate/README.md
Normal file
12
examples/N1-Composer-Translate/README.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
## Translate
|
||||
|
||||
A sample package for Nylas Mail that is translates draft text to other languages using the Yandex Translation API.
|
||||
|
||||
#### To build documentation (the manual way):
|
||||
|
||||
```
|
||||
cjsx-transform lib/main.cjsx > docs/main.coffee
|
||||
docco docs/main.coffee
|
||||
rm docs/main.coffee
|
||||
```
|
518
examples/N1-Composer-Translate/docs/docco.css
Normal file
518
examples/N1-Composer-Translate/docs/docco.css
Normal file
|
@ -0,0 +1,518 @@
|
|||
/*--------------------- Typography ----------------------------*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'aller-light';
|
||||
src: url('public/fonts/aller-light.eot');
|
||||
src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
|
||||
url('public/fonts/aller-light.woff') format('woff'),
|
||||
url('public/fonts/aller-light.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'aller-bold';
|
||||
src: url('public/fonts/aller-bold.eot');
|
||||
src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
|
||||
url('public/fonts/aller-bold.woff') format('woff'),
|
||||
url('public/fonts/aller-bold.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'roboto-black';
|
||||
src: url('public/fonts/roboto-black.eot');
|
||||
src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'),
|
||||
url('public/fonts/roboto-black.woff') format('woff'),
|
||||
url('public/fonts/roboto-black.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/*--------------------- Layout ----------------------------*/
|
||||
html { height: 100%; }
|
||||
body {
|
||||
font-family: "aller-light";
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: #30404f;
|
||||
margin: 0; padding: 0;
|
||||
height:100%;
|
||||
}
|
||||
#container { min-height: 100%; }
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: normal;
|
||||
font-family: "aller-bold";
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 15px 0 0px;
|
||||
}
|
||||
.annotation ul, .annotation ol {
|
||||
margin: 25px 0;
|
||||
}
|
||||
.annotation ul li, .annotation ol li {
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #112233;
|
||||
line-height: 1em;
|
||||
font-weight: normal;
|
||||
font-family: "roboto-black";
|
||||
text-transform: uppercase;
|
||||
margin: 30px 0 15px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 40px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.26em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
background: 1px #ddd;
|
||||
height: 1px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
pre, tt, code {
|
||||
font-size: 12px; line-height: 16px;
|
||||
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
.annotation pre {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 7px 10px;
|
||||
background: #fcfcfc;
|
||||
-moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
|
||||
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.annotation pre code {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
border-left: 5px solid #ccc;
|
||||
margin: 0;
|
||||
padding: 1px 0 1px 1em;
|
||||
}
|
||||
.sections blockquote p {
|
||||
font-family: Menlo, Consolas, Monaco, monospace;
|
||||
font-size: 12px; line-height: 16px;
|
||||
color: #999;
|
||||
margin: 10px 0 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
ul.sections {
|
||||
list-style: none;
|
||||
padding:0 0 5px 0;;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
/*
|
||||
Force border-box so that % widths fit the parent
|
||||
container without overlap because of margin/padding.
|
||||
|
||||
More Info : http://www.quirksmode.org/css/box.html
|
||||
*/
|
||||
ul.sections > li > div {
|
||||
-moz-box-sizing: border-box; /* firefox */
|
||||
-ms-box-sizing: border-box; /* ie */
|
||||
-webkit-box-sizing: border-box; /* webkit */
|
||||
-khtml-box-sizing: border-box; /* konqueror */
|
||||
box-sizing: border-box; /* css3 */
|
||||
}
|
||||
|
||||
|
||||
/*---------------------- Jump Page -----------------------------*/
|
||||
#jump_to, #jump_page {
|
||||
margin: 0;
|
||||
background: white;
|
||||
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
|
||||
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
|
||||
font: 16px Arial;
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#jump_to a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#jump_to a.large {
|
||||
display: none;
|
||||
}
|
||||
#jump_to a.small {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #676767;
|
||||
}
|
||||
|
||||
#jump_to, #jump_wrapper {
|
||||
position: fixed;
|
||||
right: 0; top: 0;
|
||||
padding: 10px 15px;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
#jump_wrapper {
|
||||
display: none;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
#jump_to:hover #jump_wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#jump_page_wrapper{
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#jump_page {
|
||||
padding: 5px 0 3px;
|
||||
margin: 0 0 25px 25px;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#jump_page .source {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
text-decoration: none;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
#jump_page .source:hover {
|
||||
background: #f5f5ff;
|
||||
}
|
||||
|
||||
#jump_page .source:first-child {
|
||||
}
|
||||
|
||||
/*---------------------- Low resolutions (> 320px) ---------------------*/
|
||||
@media only screen and (min-width: 320px) {
|
||||
.pilwrap { display: none; }
|
||||
|
||||
ul.sections > li > div {
|
||||
display: block;
|
||||
padding:5px 10px 0 10px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.content {
|
||||
overflow-x:auto;
|
||||
-webkit-box-shadow: inset 0 0 5px #e5e5ee;
|
||||
box-shadow: inset 0 0 5px #e5e5ee;
|
||||
border: 1px solid #dedede;
|
||||
margin:5px 10px 5px 10px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation pre {
|
||||
margin: 7px 0 7px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation p tt, .annotation code {
|
||||
background: #f8f8ff;
|
||||
border: 1px solid #dedede;
|
||||
font-size: 12px;
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------- (> 481px) ---------------------*/
|
||||
@media only screen and (min-width: 481px) {
|
||||
#container {
|
||||
position: relative;
|
||||
}
|
||||
body {
|
||||
background-color: #F5F5FF;
|
||||
font-size: 15px;
|
||||
line-height: 21px;
|
||||
}
|
||||
pre, tt, code {
|
||||
line-height: 18px;
|
||||
}
|
||||
p, ul, ol {
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
|
||||
|
||||
#jump_to {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#jump_wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
#jump_to, #jump_page {
|
||||
font: 10px Arial;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
#jump_page .source {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#jump_to a.large {
|
||||
display: inline-block;
|
||||
}
|
||||
#jump_to a.small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#background {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
width: 350px;
|
||||
background: #fff;
|
||||
border-right: 1px solid #e5e5ee;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
ul.sections > li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ul.sections > li > div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation {
|
||||
max-width: 350px;
|
||||
min-width: 350px;
|
||||
min-height: 5px;
|
||||
padding: 13px;
|
||||
overflow-x: hidden;
|
||||
white-space: normal;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
ul.sections > li > div.annotation pre {
|
||||
margin: 15px 0 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.content {
|
||||
padding: 13px;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.pilwrap {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.pilcrow {
|
||||
font: 12px Arial;
|
||||
text-decoration: none;
|
||||
color: #454545;
|
||||
position: absolute;
|
||||
top: 3px; left: -20px;
|
||||
padding: 1px 2px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
}
|
||||
.for-h1 .pilcrow {
|
||||
top: 47px;
|
||||
}
|
||||
.for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
|
||||
top: 35px;
|
||||
}
|
||||
|
||||
ul.sections > li > div.annotation:hover .pilcrow {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------- (> 1025px) ---------------------*/
|
||||
@media only screen and (min-width: 1025px) {
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
#background {
|
||||
width: 525px;
|
||||
}
|
||||
ul.sections > li > div.annotation {
|
||||
max-width: 525px;
|
||||
min-width: 525px;
|
||||
padding: 10px 25px 1px 50px;
|
||||
}
|
||||
ul.sections > li > div.content {
|
||||
padding: 9px 15px 16px 25px;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------- Syntax Highlighting -----------------------------*/
|
||||
|
||||
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
|
||||
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
|
||||
/*
|
||||
|
||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
||||
|
||||
*/
|
||||
|
||||
pre code {
|
||||
display: block; padding: 0.5em;
|
||||
color: #000;
|
||||
background: #f8f8ff
|
||||
}
|
||||
|
||||
pre .hljs-comment,
|
||||
pre .hljs-template_comment,
|
||||
pre .hljs-diff .hljs-header,
|
||||
pre .hljs-javadoc {
|
||||
color: #408080;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
pre .hljs-keyword,
|
||||
pre .hljs-assignment,
|
||||
pre .hljs-literal,
|
||||
pre .hljs-css .hljs-rule .hljs-keyword,
|
||||
pre .hljs-winutils,
|
||||
pre .hljs-javascript .hljs-title,
|
||||
pre .hljs-lisp .hljs-title,
|
||||
pre .hljs-subst {
|
||||
color: #954121;
|
||||
/*font-weight: bold*/
|
||||
}
|
||||
|
||||
pre .hljs-number,
|
||||
pre .hljs-hexcolor {
|
||||
color: #40a070
|
||||
}
|
||||
|
||||
pre .hljs-string,
|
||||
pre .hljs-tag .hljs-value,
|
||||
pre .hljs-phpdoc,
|
||||
pre .hljs-tex .hljs-formula {
|
||||
color: #219161;
|
||||
}
|
||||
|
||||
pre .hljs-title,
|
||||
pre .hljs-id {
|
||||
color: #19469D;
|
||||
}
|
||||
pre .hljs-params {
|
||||
color: #00F;
|
||||
}
|
||||
|
||||
pre .hljs-javascript .hljs-title,
|
||||
pre .hljs-lisp .hljs-title,
|
||||
pre .hljs-subst {
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
pre .hljs-class .hljs-title,
|
||||
pre .hljs-haskell .hljs-label,
|
||||
pre .hljs-tex .hljs-command {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .hljs-tag,
|
||||
pre .hljs-tag .hljs-title,
|
||||
pre .hljs-rules .hljs-property,
|
||||
pre .hljs-django .hljs-tag .hljs-keyword {
|
||||
color: #000080;
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
pre .hljs-attribute,
|
||||
pre .hljs-variable,
|
||||
pre .hljs-instancevar,
|
||||
pre .hljs-lisp .hljs-body {
|
||||
color: #008080
|
||||
}
|
||||
|
||||
pre .hljs-regexp {
|
||||
color: #B68
|
||||
}
|
||||
|
||||
pre .hljs-class {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .hljs-symbol,
|
||||
pre .hljs-ruby .hljs-symbol .hljs-string,
|
||||
pre .hljs-ruby .hljs-symbol .hljs-keyword,
|
||||
pre .hljs-ruby .hljs-symbol .hljs-keymethods,
|
||||
pre .hljs-lisp .hljs-keyword,
|
||||
pre .hljs-tex .hljs-special,
|
||||
pre .hljs-input_number {
|
||||
color: #990073
|
||||
}
|
||||
|
||||
pre .hljs-builtin,
|
||||
pre .hljs-constructor,
|
||||
pre .hljs-built_in,
|
||||
pre .hljs-lisp .hljs-title {
|
||||
color: #0086b3
|
||||
}
|
||||
|
||||
pre .hljs-preprocessor,
|
||||
pre .hljs-pi,
|
||||
pre .hljs-doctype,
|
||||
pre .hljs-shebang,
|
||||
pre .hljs-cdata {
|
||||
color: #999;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre .hljs-deletion {
|
||||
background: #fdd
|
||||
}
|
||||
|
||||
pre .hljs-addition {
|
||||
background: #dfd
|
||||
}
|
||||
|
||||
pre .hljs-diff .hljs-change {
|
||||
background: #0086b3
|
||||
}
|
||||
|
||||
pre .hljs-chunk {
|
||||
color: #aaa
|
||||
}
|
||||
|
||||
pre .hljs-tex .hljs-formula {
|
||||
opacity: 0.5;
|
||||
}
|
145
examples/N1-Composer-Translate/docs/main.coffee
Normal file
145
examples/N1-Composer-Translate/docs/main.coffee
Normal file
|
@ -0,0 +1,145 @@
|
|||
# # Translation Plugin
|
||||
# Last Revised: April 23, 2015 by Ben Gotow
|
||||
#
|
||||
# TranslateButton is a simple React component that allows you to select
|
||||
# a language from a popup menu and translates draft text into that language.
|
||||
#
|
||||
|
||||
request = require 'request'
|
||||
|
||||
{Utils,
|
||||
React,
|
||||
ComponentRegistry,
|
||||
DraftStore} = require 'nylas-exports'
|
||||
{Menu,
|
||||
RetinaImg,
|
||||
Popover} = require 'nylas-component-kit'
|
||||
|
||||
YandexTranslationURL = 'https://translate.yandex.net/api/v1.5/tr.json/translate'
|
||||
YandexTranslationKey = 'trnsl.1.1.20150415T044616Z.24814c314120d022.0a339e2bc2d2337461a98d5ec9863fc46e42735e'
|
||||
YandexLanguages =
|
||||
'English': 'en'
|
||||
'Spanish': 'es'
|
||||
'Russian': 'ru'
|
||||
'Chinese': 'zh'
|
||||
'French': 'fr'
|
||||
'German': 'de'
|
||||
'Italian': 'it'
|
||||
'Japanese': 'ja'
|
||||
'Portuguese': 'pt'
|
||||
'Korean': 'ko'
|
||||
|
||||
class TranslateButton extends React.Component
|
||||
|
||||
# Adding a `displayName` makes debugging React easier
|
||||
@displayName: 'TranslateButton'
|
||||
|
||||
# Since our button is being injected into the Composer Footer,
|
||||
# we receive the local id of the current draft as a `prop` (a read-only
|
||||
# property). Since our code depends on this prop, we mark it as a requirement.
|
||||
#
|
||||
@propTypes:
|
||||
draftLocalId: React.PropTypes.string.isRequired
|
||||
|
||||
# The `render` method returns a React Virtual DOM element. This code looks
|
||||
# like HTML, but don't be fooled. The CJSX preprocessor converts
|
||||
#
|
||||
# `<a href="http://facebook.github.io/react/">Hello!</a>`
|
||||
#
|
||||
# into Javascript objects which describe the HTML you want:
|
||||
#
|
||||
# `React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')`
|
||||
#
|
||||
# We're rendering a `Popover` with a `Menu` inside. These components are part
|
||||
# of Edgehill's standard `nylas-component-kit` library, and make it easy to build
|
||||
# interfaces that match the rest of Edgehill's UI.
|
||||
#
|
||||
render: =>
|
||||
React.createElement(Popover, {"ref": "popover", \
|
||||
"className": "translate-language-picker pull-right", \
|
||||
"buttonComponent": (@_renderButton())},
|
||||
React.createElement(Menu, {"items": ( Object.keys(YandexLanguages) ), \
|
||||
"itemKey": ( (item) -> item ), \
|
||||
"itemContent": ( (item) -> item ), \
|
||||
"onSelect": (@_onTranslate)
|
||||
})
|
||||
)
|
||||
|
||||
# Helper method to render the button that will activate the popover. Using the
|
||||
# `RetinaImg` component makes it easy to display an image from our package.
|
||||
# `RetinaImg` will automatically chose the best image format for our display.
|
||||
#
|
||||
_renderButton: =>
|
||||
React.createElement("button", {"className": "btn btn-toolbar"}, """
|
||||
Translate
|
||||
""", React.createElement(RetinaImg, {"name": "toolbar-chevron.png"})
|
||||
)
|
||||
|
||||
_onTranslate: (lang) =>
|
||||
@refs.popover.close()
|
||||
|
||||
# Obtain the session for the current draft. The draft session provides us
|
||||
# the draft object and also manages saving changes to the local cache and
|
||||
# Nilas API as multiple parts of the application touch the draft.
|
||||
#
|
||||
session = DraftStore.sessionForLocalId(@props.draftLocalId)
|
||||
session.prepare().then =>
|
||||
body = session.draft().body
|
||||
bodyQuoteStart = Utils.quotedTextIndex(body)
|
||||
|
||||
# Identify the text we want to translate. We need to make sure we
|
||||
# don't translate quoted text.
|
||||
if bodyQuoteStart > 0
|
||||
text = body.substr(0, bodyQuoteStart)
|
||||
else
|
||||
text = body
|
||||
|
||||
query =
|
||||
key: YandexTranslationKey
|
||||
lang: YandexLanguages[lang]
|
||||
text: text
|
||||
format: 'html'
|
||||
|
||||
# Use Node's `request` library to perform the translation using the Yandex API.
|
||||
request {url: YandexTranslationURL, qs: query}, (error, resp, data) =>
|
||||
return @_onError(error) unless resp.statusCode is 200
|
||||
json = JSON.parse(data)
|
||||
|
||||
# The new text of the draft is our translated response, plus any quoted text
|
||||
# that we didn't process.
|
||||
translated = json.text.join('')
|
||||
translated += body.substr(bodyQuoteStart) if bodyQuoteStart > 0
|
||||
|
||||
# To update the draft, we add the new body to it's session. The session object
|
||||
# automatically marshalls changes to the database and ensures that others accessing
|
||||
# the same draft are notified of changes.
|
||||
session.changes.add(body: translated)
|
||||
session.changes.commit()
|
||||
|
||||
_onError: (error) =>
|
||||
@refs.popover.close()
|
||||
dialog = require('remote').require('dialog')
|
||||
dialog.showErrorBox('Geolocation Failed', error.toString())
|
||||
|
||||
|
||||
module.exports =
|
||||
# Activate is called when the package is loaded. If your package previously
|
||||
# saved state using `serialize` it is provided.
|
||||
#
|
||||
activate: (@state) ->
|
||||
ComponentRegistry.register TranslateButton,
|
||||
role: 'Composer:ActionButton'
|
||||
|
||||
# Serialize is called when your package is about to be unmounted.
|
||||
# You can return a state object that will be passed back to your package
|
||||
# when it is re-activated.
|
||||
#
|
||||
serialize: ->
|
||||
|
||||
# This **optional** method is called when the window is shutting down,
|
||||
# or when your package is being updated or disabled. If your package is
|
||||
# watching any files, holding external resources, providing commands or
|
||||
# subscribing to events, release them here.
|
||||
#
|
||||
deactivate: ->
|
||||
ComponentRegistry.unregister(TranslateButton)
|
312
examples/N1-Composer-Translate/docs/main.html
Normal file
312
examples/N1-Composer-Translate/docs/main.html
Normal file
|
@ -0,0 +1,312 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Translation Plugin</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
|
||||
<link rel="stylesheet" media="all" href="docco.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="background"></div>
|
||||
|
||||
<ul class="sections">
|
||||
|
||||
|
||||
|
||||
<li id="section-1">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-1">¶</a>
|
||||
</div>
|
||||
<h1 id="translation-plugin">Translation Plugin</h1>
|
||||
<p>Last Revised: April 23, 2015 by Ben Gotow</p>
|
||||
<p>TranslateButton is a simple React component that allows you to select
|
||||
a language from a popup menu and translates draft text into that language.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre>
|
||||
request = <span class="hljs-built_in">require</span> <span class="hljs-string">'request'</span>
|
||||
|
||||
{Utils,
|
||||
React,
|
||||
ComponentRegistry,
|
||||
DraftStore} = <span class="hljs-built_in">require</span> <span class="hljs-string">'nylas-exports'</span>
|
||||
{Menu,
|
||||
RetinaImg,
|
||||
Popover} = <span class="hljs-built_in">require</span> <span class="hljs-string">'nylas-component-kit'</span>
|
||||
|
||||
YandexTranslationURL = <span class="hljs-string">'https://translate.yandex.net/api/v1.5/tr.json/translate'</span>
|
||||
YandexTranslationKey = <span class="hljs-string">'trnsl.1.1.20150415T044616Z.24814c314120d022.0a339e2bc2d2337461a98d5ec9863fc46e42735e'</span>
|
||||
YandexLanguages =
|
||||
<span class="hljs-string">'English'</span>: <span class="hljs-string">'en'</span>
|
||||
<span class="hljs-string">'Spanish'</span>: <span class="hljs-string">'es'</span>
|
||||
<span class="hljs-string">'Russian'</span>: <span class="hljs-string">'ru'</span>
|
||||
<span class="hljs-string">'Chinese'</span>: <span class="hljs-string">'zh'</span>
|
||||
<span class="hljs-string">'French'</span>: <span class="hljs-string">'fr'</span>
|
||||
<span class="hljs-string">'German'</span>: <span class="hljs-string">'de'</span>
|
||||
<span class="hljs-string">'Italian'</span>: <span class="hljs-string">'it'</span>
|
||||
<span class="hljs-string">'Japanese'</span>: <span class="hljs-string">'ja'</span>
|
||||
<span class="hljs-string">'Portuguese'</span>: <span class="hljs-string">'pt'</span>
|
||||
<span class="hljs-string">'Korean'</span>: <span class="hljs-string">'ko'</span>
|
||||
|
||||
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TranslateButton</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span></span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-2">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-2">¶</a>
|
||||
</div>
|
||||
<p>Adding a <code>displayName</code> makes debugging React easier</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@displayName</span>: <span class="hljs-string">'TranslateButton'</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-3">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-3">¶</a>
|
||||
</div>
|
||||
<p>Since our button is being injected into the Composer Footer,
|
||||
we receive the local id of the current draft as a <code>prop</code> (a read-only
|
||||
property). Since our code depends on this prop, we mark it as a requirement.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@propTypes</span>:
|
||||
<span class="hljs-attribute">draftLocalId</span>: React.PropTypes.string.isRequired</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-4">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-4">¶</a>
|
||||
</div>
|
||||
<p>The <code>render</code> method returns a React Virtual DOM element. This code looks
|
||||
like HTML, but don’t be fooled. The CJSX preprocessor converts</p>
|
||||
<p><code><a href="http://facebook.github.io/react/">Hello!</a></code></p>
|
||||
<p>into Javascript objects which describe the HTML you want:</p>
|
||||
<p><code>React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')</code></p>
|
||||
<p>We’re rendering a <code>Popover</code> with a <code>Menu</code> inside. These components are part
|
||||
of Edgehill’s standard <code>nylas-component-kit</code> library, and make it easy to build
|
||||
interfaces that match the rest of Edgehill’s UI.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">render</span>: <span class="hljs-function">=></span>
|
||||
React.createElement(Popover, {<span class="hljs-string">"ref"</span>: <span class="hljs-string">"popover"</span>, \
|
||||
<span class="hljs-string">"className"</span>: <span class="hljs-string">"translate-language-picker pull-right"</span>, \
|
||||
<span class="hljs-string">"buttonComponent"</span>: (<span class="hljs-property">@_renderButton</span>())},
|
||||
React.createElement(Menu, {<span class="hljs-string">"items"</span>: ( Object.keys(YandexLanguages) ), \
|
||||
<span class="hljs-string">"itemKey"</span>: <span class="hljs-function"><span class="hljs-params">( (item) -> item )</span>, \
|
||||
"itemContent": <span class="hljs-params">( (item) -> item )</span>, \
|
||||
"onSelect": <span class="hljs-params">(<span class="hljs-property">@_onTranslate</span>)</span>
|
||||
})
|
||||
)
|
||||
|
||||
</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-5">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-5">¶</a>
|
||||
</div>
|
||||
<p>Helper method to render the button that will activate the popover. Using the
|
||||
<code>RetinaImg</code> component makes it easy to display an image from our package.
|
||||
<code>RetinaImg</code> will automatically chose the best image format for our display.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">_renderButton</span>: <span class="hljs-function">=></span>
|
||||
React.createElement(<span class="hljs-string">"button"</span>, {<span class="hljs-string">"className"</span>: <span class="hljs-string">"btn btn-toolbar"</span>}, <span class="hljs-string">"""
|
||||
Translate
|
||||
"""</span>, React.createElement(RetinaImg, {<span class="hljs-string">"name"</span>: <span class="hljs-string">"toolbar-chevron.png"</span>})
|
||||
)
|
||||
|
||||
<span class="hljs-attribute">_onTranslate</span>: <span class="hljs-function"><span class="hljs-params">(lang)</span> =></span>
|
||||
<span class="hljs-property">@refs</span>.popover.close()</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-6">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-6">¶</a>
|
||||
</div>
|
||||
<p>Obtain the session for the current draft. The draft session provides us
|
||||
the draft object and also manages saving changes to the local cache and
|
||||
Nilas API as multiple parts of the application touch the draft.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> session = DraftStore.sessionForLocalId(<span class="hljs-property">@props</span>.draftLocalId)
|
||||
session.prepare().<span class="hljs-keyword">then</span> =>
|
||||
body = session.draft().body
|
||||
bodyQuoteStart = Utils.quotedTextIndex(body)</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-7">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-7">¶</a>
|
||||
</div>
|
||||
<p>Identify the text we want to translate. We need to make sure we
|
||||
don’t translate quoted text.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> bodyQuoteStart > <span class="hljs-number">0</span>
|
||||
text = body.substr(<span class="hljs-number">0</span>, bodyQuoteStart)
|
||||
<span class="hljs-keyword">else</span>
|
||||
text = body
|
||||
|
||||
query =
|
||||
<span class="hljs-attribute">key</span>: YandexTranslationKey
|
||||
<span class="hljs-attribute">lang</span>: YandexLanguages[lang]
|
||||
<span class="hljs-attribute">text</span>: text
|
||||
<span class="hljs-attribute">format</span>: <span class="hljs-string">'html'</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-8">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-8">¶</a>
|
||||
</div>
|
||||
<p>Use Node’s <code>request</code> library to perform the translation using the Yandex API.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> request {<span class="hljs-attribute">url</span>: YandexTranslationURL, <span class="hljs-attribute">qs</span>: query}, <span class="hljs-function"><span class="hljs-params">(error, resp, data)</span> =></span>
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-property">@_onError</span>(error) <span class="hljs-keyword">unless</span> resp.statusCode <span class="hljs-keyword">is</span> <span class="hljs-number">200</span>
|
||||
json = JSON.parse(data)</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-9">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-9">¶</a>
|
||||
</div>
|
||||
<p>The new text of the draft is our translated response, plus any quoted text
|
||||
that we didn’t process.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> translated = json.text.join(<span class="hljs-string">''</span>)
|
||||
translated += body.substr(bodyQuoteStart) <span class="hljs-keyword">if</span> bodyQuoteStart > <span class="hljs-number">0</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-10">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-10">¶</a>
|
||||
</div>
|
||||
<p>To update the draft, we add the new body to it’s session. The session object
|
||||
automatically marshalls changes to the database and ensures that others accessing
|
||||
the same draft are notified of changes.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> session.changes.add(<span class="hljs-attribute">body</span>: translated)
|
||||
session.changes.commit()
|
||||
|
||||
<span class="hljs-attribute">_onError</span>: <span class="hljs-function"><span class="hljs-params">(error)</span> =></span>
|
||||
<span class="hljs-property">@refs</span>.popover.close()
|
||||
dialog = <span class="hljs-built_in">require</span>(<span class="hljs-string">'remote'</span>).<span class="hljs-built_in">require</span>(<span class="hljs-string">'dialog'</span>)
|
||||
dialog.showErrorBox(<span class="hljs-string">'Geolocation Failed'</span>, error.toString())
|
||||
|
||||
|
||||
<span class="hljs-built_in">module</span>.exports =</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-11">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-11">¶</a>
|
||||
</div>
|
||||
<p>Activate is called when the package is loaded. If your package previously
|
||||
saved state using <code>serialize</code> it is provided.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">activate</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-property">@state</span>)</span> -></span>
|
||||
ComponentRegistry.register TranslateButton,
|
||||
<span class="hljs-attribute">role</span>: <span class="hljs-string">'Composer:ActionButton'</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-12">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-12">¶</a>
|
||||
</div>
|
||||
<p>Serialize is called when your package is about to be unmounted.
|
||||
You can return a state object that will be passed back to your package
|
||||
when it is re-activated.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">serialize</span>: <span class="hljs-function">-></span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-13">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-13">¶</a>
|
||||
</div>
|
||||
<p>This <strong>optional</strong> method is called when the window is shutting down,
|
||||
or when your package is being updated or disabled. If your package is
|
||||
watching any files, holding external resources, providing commands or
|
||||
subscribing to events, release them here.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">deactivate</span>: <span class="hljs-function">-></span>
|
||||
ComponentRegistry.unregister(TranslateButton)</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-bold.eot
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-bold.eot
Normal file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-bold.ttf
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-bold.ttf
Normal file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-bold.woff
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-bold.woff
Normal file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-light.eot
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-light.eot
Normal file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-light.ttf
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/aller-light.ttf
Normal file
Binary file not shown.
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/fleurons.eot
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/fleurons.eot
Normal file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/fleurons.ttf
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/fleurons.ttf
Normal file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/fleurons.woff
Normal file
BIN
examples/N1-Composer-Translate/docs/public/fonts/fleurons.woff
Normal file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/roboto-black.eot
Executable file
BIN
examples/N1-Composer-Translate/docs/public/fonts/roboto-black.eot
Executable file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/roboto-black.ttf
Executable file
BIN
examples/N1-Composer-Translate/docs/public/fonts/roboto-black.ttf
Executable file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/fonts/roboto-black.woff
Executable file
BIN
examples/N1-Composer-Translate/docs/public/fonts/roboto-black.woff
Executable file
Binary file not shown.
BIN
examples/N1-Composer-Translate/docs/public/images/gray.png
Normal file
BIN
examples/N1-Composer-Translate/docs/public/images/gray.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
375
examples/N1-Composer-Translate/docs/public/stylesheets/normalize.css
vendored
Normal file
375
examples/N1-Composer-Translate/docs/public/stylesheets/normalize.css
vendored
Normal file
|
@ -0,0 +1,375 @@
|
|||
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
|
||||
|
||||
/* ==========================================================================
|
||||
HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Corrects `block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects `inline-block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling for `hidden` attribute not present in IE 8/9.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Base
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* 1. Sets default font family to sans-serif.
|
||||
* 2. Prevents iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes default margin.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Links
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses `outline` inconsistency between Chrome and other browsers.
|
||||
*/
|
||||
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Improves readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Typography
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
|
||||
* Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in IE 8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Corrects font family set oddly in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Improves readability of pre-formatted text in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets consistent quote types.
|
||||
*/
|
||||
|
||||
q {
|
||||
quotes: "\201C" "\201D" "\2018" "\2019";
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Removes border when inside `a` element in IE 8/9.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects overflow displayed oddly in IE 9.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Figures
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses margin not present in IE 8/9 and Safari 5.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Forms
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Corrects color not being inherited in IE 8/9.
|
||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Corrects font family not being inherited in all browsers.
|
||||
* 2. Corrects font size not being inherited in all browsers.
|
||||
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 2 */
|
||||
margin: 0; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Corrects inability to style clickable `input` types in iOS.
|
||||
* 3. Improves usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Addresses box sizing set to `content-box` in IE 8/9.
|
||||
* 2. Removes excess padding in IE 8/9.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
|
||||
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
|
||||
* (include `-moz` to future-proof).
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box; /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes inner padding and search cancel button in Safari 5 and Chrome
|
||||
* on OS X.
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes inner padding and border in Firefox 4+.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Removes default vertical scrollbar in IE 8/9.
|
||||
* 2. Improves readability and alignment in all browsers.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto; /* 1 */
|
||||
vertical-align: top; /* 2 */
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Tables
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
137
examples/N1-Composer-Translate/lib/main.cjsx
Normal file
137
examples/N1-Composer-Translate/lib/main.cjsx
Normal file
|
@ -0,0 +1,137 @@
|
|||
# # Translation Plugin
|
||||
# Last Revised: April 23, 2015 by Ben Gotow
|
||||
#
|
||||
# TranslateButton is a simple React component that allows you to select
|
||||
# a language from a popup menu and translates draft text into that language.
|
||||
#
|
||||
|
||||
request = require 'request'
|
||||
|
||||
{React,
|
||||
ComponentRegistry,
|
||||
QuotedHTMLParser,
|
||||
DraftStore} = require 'nylas-exports'
|
||||
{Menu,
|
||||
RetinaImg,
|
||||
Popover} = require 'nylas-component-kit'
|
||||
|
||||
YandexTranslationURL = 'https://translate.yandex.net/api/v1.5/tr.json/translate'
|
||||
YandexTranslationKey = 'trnsl.1.1.20150415T044616Z.24814c314120d022.0a339e2bc2d2337461a98d5ec9863fc46e42735e'
|
||||
YandexLanguages =
|
||||
'English': 'en'
|
||||
'Spanish': 'es'
|
||||
'Russian': 'ru'
|
||||
'Chinese': 'zh'
|
||||
'French': 'fr'
|
||||
'German': 'de'
|
||||
'Italian': 'it'
|
||||
'Japanese': 'ja'
|
||||
'Portuguese': 'pt'
|
||||
'Korean': 'ko'
|
||||
|
||||
class TranslateButton extends React.Component
|
||||
|
||||
# Adding a `displayName` makes debugging React easier
|
||||
@displayName: 'TranslateButton'
|
||||
|
||||
# Since our button is being injected into the Composer Footer,
|
||||
# we receive the local id of the current draft as a `prop` (a read-only
|
||||
# property). Since our code depends on this prop, we mark it as a requirement.
|
||||
#
|
||||
@propTypes:
|
||||
draftClientId: React.PropTypes.string.isRequired
|
||||
|
||||
# The `render` method returns a React Virtual DOM element. This code looks
|
||||
# like HTML, but don't be fooled. The CJSX preprocessor converts
|
||||
#
|
||||
# `<a href="http://facebook.github.io/react/">Hello!</a>`
|
||||
#
|
||||
# into Javascript objects which describe the HTML you want:
|
||||
#
|
||||
# `React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')`
|
||||
#
|
||||
# We're rendering a `Popover` with a `Menu` inside. These components are part
|
||||
# of Edgehill's standard `nylas-component-kit` library, and make it easy to build
|
||||
# interfaces that match the rest of Edgehill's UI.
|
||||
#
|
||||
render: =>
|
||||
<Popover ref="popover"
|
||||
className="translate-language-picker pull-right"
|
||||
buttonComponent={@_renderButton()}>
|
||||
<Menu items={ Object.keys(YandexLanguages) }
|
||||
itemKey={ (item) -> item }
|
||||
itemContent={ (item) -> item }
|
||||
onSelect={@_onTranslate}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
# Helper method to render the button that will activate the popover. Using the
|
||||
# `RetinaImg` component makes it easy to display an image from our package.
|
||||
# `RetinaImg` will automatically chose the best image format for our display.
|
||||
#
|
||||
_renderButton: =>
|
||||
<button className="btn btn-toolbar">
|
||||
Translate
|
||||
<RetinaImg name="toolbar-chevron.png"/>
|
||||
</button>
|
||||
|
||||
_onTranslate: (lang) =>
|
||||
@refs.popover.close()
|
||||
|
||||
# Obtain the session for the current draft. The draft session provides us
|
||||
# the draft object and also manages saving changes to the local cache and
|
||||
# Nilas API as multiple parts of the application touch the draft.
|
||||
#
|
||||
session = DraftStore.sessionForClientId(@props.draftClientId).then (session) =>
|
||||
draftHtml = session.draft().body
|
||||
text = QuotedHTMLParser.removeQuotedHTML(draftHtml)
|
||||
|
||||
query =
|
||||
key: YandexTranslationKey
|
||||
lang: YandexLanguages[lang]
|
||||
text: text
|
||||
format: 'html'
|
||||
|
||||
# Use Node's `request` library to perform the translation using the Yandex API.
|
||||
request {url: YandexTranslationURL, qs: query}, (error, resp, data) =>
|
||||
return @_onError(error) unless resp.statusCode is 200
|
||||
json = JSON.parse(data)
|
||||
|
||||
# The new text of the draft is our translated response, plus any quoted text
|
||||
# that we didn't process.
|
||||
translated = json.text.join('')
|
||||
translated = QuotedHTMLParser.appendQuotedHTML(translated, draftHtml)
|
||||
|
||||
# To update the draft, we add the new body to it's session. The session object
|
||||
# automatically marshalls changes to the database and ensures that others accessing
|
||||
# the same draft are notified of changes.
|
||||
session.changes.add(body: translated)
|
||||
session.changes.commit()
|
||||
|
||||
_onError: (error) =>
|
||||
@refs.popover.close()
|
||||
dialog = require('remote').require('dialog')
|
||||
dialog.showErrorBox('Language Conversion Failed', error.toString())
|
||||
|
||||
|
||||
module.exports =
|
||||
# Activate is called when the package is loaded. If your package previously
|
||||
# saved state using `serialize` it is provided.
|
||||
#
|
||||
activate: (@state) ->
|
||||
ComponentRegistry.register TranslateButton,
|
||||
role: 'Composer:ActionButton'
|
||||
|
||||
# Serialize is called when your package is about to be unmounted.
|
||||
# You can return a state object that will be passed back to your package
|
||||
# when it is re-activated.
|
||||
#
|
||||
serialize: ->
|
||||
|
||||
# This **optional** method is called when the window is shutting down,
|
||||
# or when your package is being updated or disabled. If your package is
|
||||
# watching any files, holding external resources, providing commands or
|
||||
# subscribing to events, release them here.
|
||||
#
|
||||
deactivate: ->
|
||||
ComponentRegistry.unregister(TranslateButton)
|
21
examples/N1-Composer-Translate/package.json
Executable file
21
examples/N1-Composer-Translate/package.json
Executable file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "translate",
|
||||
"version": "0.2.0",
|
||||
"main": "./lib/main",
|
||||
"description": "An example package for Nylas Mail that translates drafts into other languages using the Yandex API.",
|
||||
"license": "Proprietary",
|
||||
"engines": {
|
||||
"atom": "*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nylas/translate"
|
||||
},
|
||||
"windowTypes": {
|
||||
"default": true,
|
||||
"composer": true
|
||||
},
|
||||
"dependencies": {
|
||||
"request": "^2.53"
|
||||
}
|
||||
}
|
12
examples/N1-Composer-Translate/spec/main-spec.coffee
Normal file
12
examples/N1-Composer-Translate/spec/main-spec.coffee
Normal file
|
@ -0,0 +1,12 @@
|
|||
describe "AccountSidebarStore", ->
|
||||
xit "should update it's selected ID when the focusTag action fires", ->
|
||||
true
|
||||
|
||||
xit "should update when the DatabaseStore emits changes to tags", ->
|
||||
true
|
||||
|
||||
xit "should update when the NamespaceStore emits", ->
|
||||
true
|
||||
|
||||
xit "should provide an array of sections to the sidebar view", ->
|
||||
true
|
13
examples/N1-Composer-Translate/stylesheets/translate.less
Normal file
13
examples/N1-Composer-Translate/stylesheets/translate.less
Normal file
|
@ -0,0 +1,13 @@
|
|||
@import "ui-variables";
|
||||
@import "ui-mixins";
|
||||
|
||||
.translate-language-picker .menu {
|
||||
.footer-container,
|
||||
.header-container {
|
||||
display:none;
|
||||
}
|
||||
.content-container {
|
||||
height:185px;
|
||||
overflow:scroll;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
examples/N1-Sidebar-Github/assets/github.png
Normal file
BIN
examples/N1-Sidebar-Github/assets/github.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
85
examples/N1-Sidebar-Github/lib/github-sidebar.cjsx
Normal file
85
examples/N1-Sidebar-Github/lib/github-sidebar.cjsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
_ = require 'underscore-plus'
|
||||
GithubUserStore = require "./github-user-store"
|
||||
{React} = require 'nylas-exports'
|
||||
|
||||
# Small React component that renders a single Github repository
|
||||
class GithubRepo extends React.Component
|
||||
@displayName: 'GithubRepo'
|
||||
@propTypes:
|
||||
# This component takes a `repo` object as a prop. Listing props is optional
|
||||
# but enables nice React warnings when our expectations aren't met
|
||||
repo: React.PropTypes.object.isRequired
|
||||
|
||||
render: =>
|
||||
<div className="repo">
|
||||
<div className="stars">{@props.repo.stargazers_count}</div>
|
||||
<a href={@props.repo.html_url}>{@props.repo.full_name}</a>
|
||||
</div>
|
||||
|
||||
# Small React component that renders the user's Github profile.
|
||||
class GithubProfile extends React.Component
|
||||
@displayName: 'GithubProfile'
|
||||
@propTypes:
|
||||
# This component takes a `profile` object as a prop. Listing props is optional
|
||||
# but enables nice React warnings when our expectations aren't met.
|
||||
profile: React.PropTypes.object.isRequired
|
||||
|
||||
render: =>
|
||||
# Transform the profile's array of repos into an array of React <GithubRepo> elements
|
||||
repoElements = _.map @props.profile.repos, (repo) ->
|
||||
<GithubRepo key={repo.id} repo={repo} />
|
||||
|
||||
# Remember - this looks like HTML, but it's actually CJSX, which is converted into
|
||||
# Coffeescript at transpile-time. We're actually creating a nested tree of Javascript
|
||||
# objects here that *represent* the DOM we want.
|
||||
<div className="profile">
|
||||
<img className="logo" src="nylas://sidebar-github-profile/assets/github.png"/>
|
||||
<a href={@props.profile.html_url}>{@props.profile.login}</a>
|
||||
<div>{repoElements}</div>
|
||||
</div>
|
||||
|
||||
module.exports =
|
||||
class GithubSidebar extends React.Component
|
||||
@displayName: 'GithubSidebar'
|
||||
|
||||
# We're registering this component to appear in one of the app's primary
|
||||
# columns, the MessageListSidebar. Each React Component in a column can
|
||||
# specify a min and max width which limit the resizing behavior of the column.
|
||||
@containerStyles:
|
||||
maxWidth: 300
|
||||
minWidth: 200
|
||||
order: 2
|
||||
flexShrink: 0
|
||||
|
||||
constructor: (@props) ->
|
||||
@state = @_getStateFromStores()
|
||||
|
||||
componentDidMount: =>
|
||||
# When our component mounts, start listening to the GithubUserStore.
|
||||
# When the store `triggers`, our `_onChange` method will fire and allow
|
||||
# us to replace our state.
|
||||
@unsubscribe = GithubUserStore.listen @_onChange
|
||||
|
||||
componentWillUnmount: =>
|
||||
@unsubscribe()
|
||||
|
||||
render: =>
|
||||
<div className="sidebar-github-profile">
|
||||
<h2>Github</h2>
|
||||
{@_renderInner()}
|
||||
</div>
|
||||
|
||||
_renderInner: =>
|
||||
# Handle various loading states by returning early
|
||||
return <div>Loading...</div> if @state.loading
|
||||
return <div>No Matching Profile</div> if not @state.profile
|
||||
<GithubProfile profile={@state.profile} />
|
||||
|
||||
# The data vended by the GithubUserStore has changed. Calling `setState:`
|
||||
# will cause React to re-render our view to reflect the new values.
|
||||
_onChange: =>
|
||||
@setState(@_getStateFromStores())
|
||||
|
||||
_getStateFromStores: =>
|
||||
profile: GithubUserStore.profileForFocusedContact()
|
||||
loading: GithubUserStore.loading()
|
82
examples/N1-Sidebar-Github/lib/github-user-store.coffee
Normal file
82
examples/N1-Sidebar-Github/lib/github-user-store.coffee
Normal file
|
@ -0,0 +1,82 @@
|
|||
_ = require 'underscore-plus'
|
||||
Reflux = require 'reflux'
|
||||
request = require 'request'
|
||||
{FocusedContactsStore} = require 'nylas-exports'
|
||||
|
||||
module.exports =
|
||||
|
||||
# This package uses the Flux pattern - our Store is a small singleton that
|
||||
# observes other parts of the application and vends data to our React
|
||||
# component. If the user could interact with the GithubSidebar, this store
|
||||
# would also listen for `Actions` emitted by our React components.
|
||||
GithubUserStore = Reflux.createStore
|
||||
|
||||
init: ->
|
||||
@_profile = null
|
||||
@_cache = {}
|
||||
@_loading = false
|
||||
@_error = null
|
||||
|
||||
# Register a callback with the FocusedContactsStore. This will tell us
|
||||
# whenever the selected person has changed so we can refresh our data.
|
||||
@listenTo FocusedContactsStore, @_onFocusedContactChanged
|
||||
|
||||
# Getter Methods
|
||||
|
||||
profileForFocusedContact: ->
|
||||
@_profile
|
||||
|
||||
loading: ->
|
||||
@_loading
|
||||
|
||||
error: ->
|
||||
@_error
|
||||
|
||||
# Called when the FocusedContactStore `triggers`, notifying us that the data
|
||||
# it vends has changed.
|
||||
_onFocusedContactChanged: ->
|
||||
# Grab the new focused contact
|
||||
contact = FocusedContactsStore.focusedContact()
|
||||
|
||||
# First, clear the contact that we're currently showing and `trigger`. Since
|
||||
# our React component observes our store, `trigger` causes our React component
|
||||
# to re-render.
|
||||
@_error = null
|
||||
@_profile = null
|
||||
|
||||
if contact
|
||||
@_profile = @_cache[contact.email]
|
||||
# Make a Github search request to find the matching user profile
|
||||
@_githubFetchProfile(contact.email) unless @_profile?
|
||||
|
||||
@trigger(@)
|
||||
|
||||
_githubFetchProfile: (email) ->
|
||||
@_loading = true
|
||||
@_githubRequest "https://api.github.com/search/users?q=#{email}", (err, resp, data) =>
|
||||
console.warn(data.message) if data.message?
|
||||
|
||||
# Sometimes we get rate limit errors, etc., so we need to check and make
|
||||
# sure we've gotten items before pulling the first one.
|
||||
profile = data?.items?[0] ? false
|
||||
|
||||
# If a profile was found, make a second request for the user's public
|
||||
# repositories.
|
||||
if profile
|
||||
profile.repos = []
|
||||
@_githubRequest profile.repos_url, (err, resp, repos) =>
|
||||
# Sort the repositories by their stars (`-` for descending order)
|
||||
profile.repos = _.sortBy repos, (repo) -> -repo.stargazers_count
|
||||
# Trigger so that our React components refresh their state and display
|
||||
# the updated data.
|
||||
@trigger(@)
|
||||
|
||||
@_loading = false
|
||||
@_profile = @_cache[email] = profile
|
||||
@trigger(@)
|
||||
|
||||
# Wrap the Node `request` library and pass the User-Agent header, which is required
|
||||
# by Github's API. Also pass `json:true`, which causes responses to be automatically
|
||||
# parsed.
|
||||
_githubRequest: (url, callback) ->
|
||||
request({url: url, headers: {'User-Agent': 'request'}, json: true}, callback)
|
30
examples/N1-Sidebar-Github/lib/main.cjsx
Normal file
30
examples/N1-Sidebar-Github/lib/main.cjsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
_ = require 'underscore-plus'
|
||||
GithubSidebar = require "./github-sidebar"
|
||||
{ComponentRegistry,
|
||||
WorkspaceStore} = require "nylas-exports"
|
||||
|
||||
module.exports =
|
||||
# Activate is called when the package is loaded. If your package previously
|
||||
# saved state using `serialize` it is provided.
|
||||
#
|
||||
activate: (@state={}) ->
|
||||
# Register our sidebar so that it appears in the Message List sidebar.
|
||||
# This sidebar is to the right of the Message List in both split pane mode
|
||||
# and list mode.
|
||||
ComponentRegistry.register GithubSidebar,
|
||||
location: WorkspaceStore.Location.MessageListSidebar
|
||||
|
||||
# Serialize is called when your package is about to be unmounted.
|
||||
# You can return a state object that will be passed back to your package
|
||||
# when it is re-activated.
|
||||
#
|
||||
serialize: ->
|
||||
|
||||
# This **optional** method is called when the window is shutting down,
|
||||
# or when your package is being updated or disabled. If your package is
|
||||
# watching any files, holding external resources, providing commands or
|
||||
# subscribing to events, release them here.
|
||||
#
|
||||
deactivate: ->
|
||||
# Unregister our component
|
||||
ComponentRegistry.unregister(GithubSidebar)
|
19
examples/N1-Sidebar-Github/package.json
Executable file
19
examples/N1-Sidebar-Github/package.json
Executable file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "sidebar-github-profile",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main",
|
||||
"description": "View github user data in the sidebar!",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"atom": "*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bengotow/nylas-sidebar-github-profile"
|
||||
},
|
||||
"dependencies": {
|
||||
"reflux": "0.1.13",
|
||||
"request": "^2.53",
|
||||
"underscore-plus": "^1.6"
|
||||
}
|
||||
}
|
BIN
examples/N1-Sidebar-Github/screenshot.png
Normal file
BIN
examples/N1-Sidebar-Github/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 224 KiB |
|
@ -0,0 +1,40 @@
|
|||
@import "ui-variables";
|
||||
|
||||
.sidebar-github-profile {
|
||||
padding: @spacing-standard;
|
||||
padding-bottom: 0;
|
||||
|
||||
a{ text-decoration: none; }
|
||||
|
||||
.logo {
|
||||
float: left;
|
||||
width:15px;
|
||||
height:15px;
|
||||
opacity:0.4;
|
||||
margin-right: @spacing-half;
|
||||
}
|
||||
|
||||
.repo {
|
||||
padding-left: @spacing-standard;
|
||||
border-left:1px solid @border-color-divider;
|
||||
margin-left: @spacing-standard/2;
|
||||
font-size: @font-size-smaller;
|
||||
a {
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
.stars {
|
||||
float:right;
|
||||
margin-left:@spacing-standard;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 11px;
|
||||
font-weight: @font-weight-semi-bold;
|
||||
text-transform: uppercase;
|
||||
color: @text-color-very-subtle;
|
||||
border-bottom: 1px solid @border-color-divider;
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue